0
votes

I have a mobile app that implements JWT authentication with the backend. Access tokens are short-lived (1h) and are not stored on the backend. Now for the refresh tokens:

  1. If refresh tokens have an expiration it means users will be periodically logged out which is highly undesirable from the business standpoint, it can harm user retention. Is there a way to avoid this without weakening the security, e.g. making refresh tokens "eternal"?

  2. What's the best way of storing and cleaning up the refresh token table that would prevent accumulation of unused tokens? Say I have the following table structure: user_id, device_id, refresh_token. If the strategy is to never expire the refresh tokens, the only way to invalidate them would be when a user logs out. However, users can also delete the app, lose or damage the device, or have their device_id changed for whatever reason. One solution I can think of is to have a refreshed_at timestamp that will allow to invalidate the tokens say after a few months of non-use. Any other known tricks?

  3. Say I use a shared secret string in addition to the refresh token when refreshing access tokens, is my understanding correct that if all 3 are compromised (access token, refresh token and shared secret), there's nothing I can do about it? What are the best practices for the refresh API call?

2

2 Answers

2
votes

Eternal refresh tokens do increase risk. To offset this, you could weaken the tokens that are issued during a refresh. To accomplish this, you could have an expiration time on a scope. Then, as the refresh happens, the new tokens have fewer and fewer scopes. Later, if the user is trying to do something high-risk, they would have to login again and prove control of their original credential. I think the business could probably live with this. (More on this idea can be found here.)

Your idea of keeping the refreshed_at is probably the way to avoid perpetual growth of the refresh token table. If the user does logout though, can't you delete the refresh tokens? There will some logout endpoint, so behind that do the cleanup. That way, your refreshed_at is only for stuff that slips between the cracks.

I don't see what the secret string does to help. It's essentially another "token" and all are bearer tokens, so I'd skip this. For more info about protecting the threats against the refresh flow, checkout section 4.5 of RFC 6819. Also, checkout section 4.12 of the OAuth security BCP (that's only an RFC).

2
votes

A couple of points to add, based on having worked with Ping Federate in the past, and having used their implementation:

  • Store a refresh token SHA256 hash rather than the token itself, so that no rogue employee can steal and use refresh tokens

  • Include the client_id and issued_at / expires_at fields. This enables an administrator to find and revoke refresh tokens by application, user and time.