Oauth2 authorization-code, multiple devices, recommended flow?

Hi!

Context
I’m working on a webapp that provides a Log-in with Twitch, as the main authentication method, I’m both: using the oauth2 authorization server to manage my user’s sessions (maintaining state) and using sometimes some Twitch API endpoints with their user tokens. So I decided to go with the authorization-code flow.

Problem
The docs are very clear and helpful with the auth flow but when it comes to user tokens they always refer to the access/refresh token pair in singular and that makes the logic easier. But In practice users can login into a website from different devices/user-agents and they would get a different token pair for each one. What is the recommended flow then? Store all the tokens pair belonging to the user in the database? I believe that some time ago, logging into one device would invalidate the rest of your tokens, this is no longer the case, but that’s why I want to make sure that my approach is right, in case this changes again in the future.

My current planned architecture that I’m working on is something like the following:

  • When a user logs in from a new user-agent via authorization code flow, store the user_id in an encrypted, httpOnly cookie, and also store the access/refresh token pair in the encrypted cookie to save some roundtrips when reading some requests (more specifically, the requests that will originate requests to Twitch API since in those cases we would get a 401 if it is invalid).
  • Store in database all the tokens pair belonging to the user. One pair per device/user-agent. This is to maintain and validate session state for stateful features, as twitch requires.
  • Have a service run every hour in theory (in practice the load is balanced across the hour minutes so I don’t validate all the users in the same minute). First delete all the expired access_tokens, then use /validate endpoint for every non-expired access token found for every user. Delete non-valid non-expired access tokens.
  • When using features that use Twitch API: when the request comes in, read user_id and token pair from encrypted cookie, if it is expired try to refresh it with the refresh token, update the corresponding token pair in the database/cookie. Database is only needed for update if expired, not reading. Use Twitch API as expected, if 401 is returned anywhere, delete all the cookies and the corresponding token pair for the user in the database, requiring user to log in again in the corresponding device.
  • When using local features that don’t need Twitch API and which are stateful; for example creating an item in my app or accessing a protected resource, check if user is still valid in my database to ensure my app hasn’t been disconnected from twitch (if it’s something really important/sensible, like payment, maybe check directly with twitch api instead of database). Check if the current access_token in the cookie matches a valid one in the database for the given user_id, if not try to refresh, if refreshed update db/cookies, if it returns 401 when using the refresh_token, delete all the cookies and ALL the tokens in the database, consider that the user has disconnected my app and redirect it to authorization code flow again. Since we run a service to validate them every hour an invalid token would last at most 1h.

Edit. Probably, it is way safer if refresh tokens are never stored in database (?)

Questions

  1. Is this the recommended flow? Is there any potential flaw in my architecture that you’d recommend changing? Since this is about security, I’m very interested in your opinions and other developers’ experiences.
  2. What is the rate limit for the /validate endpoint. As I said, I’m balancing the validations across the hour cycle period to be more gentle with twitch servers and not performing all the access_token validations at once, also I’m excluding expired access_tokens which I expect to lower the number of validations by a lot and also that would exclude non-active users, but I’m still interested in the rate limit so I can adjust better my balancer parameters and have plans to scale better, etc.

While the authentication examples are very helpful I miss some examples with the refresh token logic and mentioning what to do with multiple tokens from multiple devices/browsers, since the logic in practice is more complicated that the presented in the examples and the relation user:token is no longer 1:1, but 1:n, this changes many things and takes many considerations, many decisions directly related with security and left up to the developer’s interpretation.

Store all token pairs you will be using in the database.

I think it’s unlikely this will change, but it might, and we’ll either find out when they tell us or 5 minutes after everything breaks.

Are you using this instead of storing the authentication session in a database? In that case, you cannot log out other sessions.
But if it’s more convenient for you to store the user’s OAuth tokens in their client-side token to save a database lookup, that isn’t wrong.

I guess you are assuming that expired tokens will remain expired unless you receive another request that requires them? In practice, you will frequently delete tokens from the database, only to re-add them later.

Huh? I don’t understand deleting the cookies from the database. The cookies are stored in the user’s browsers, so you can’t anyway, but what are you storing in the database? I thought the purpose of putting all that information in the cookie is to avoid a database lookup.

In the worst-case scenario, where your whole application is compromised, not storing refresh tokens may limit the damage to some extent. On the other hand, you are supposed to be able to revoke refresh tokens, which will revoke all associated access tokens. But the documentation implies that Twitch does not support that and I haven’t tested it.

Do what works for you. Since you are concerned about security, I recommend using flowcharts, tests, and as simple code as possible.

IIRC, the rate limit is along the lines of “try to be reasonable”.

On the other hand, it provides additional flexibility to do things in ways that work best for your application. For example, if it would work for you, it may be much easier to use a single token for most of your work and let the user log in with tokens you only use once (for verifying they are a Twitch user and who they are) and then discard.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.