Stolen access tokens with refresh tokens

If a user has an access token that is a JWT with an expiry then if the JWT were to be compromised then it can only be used until the expiry is reached. This is good.

However, if refresh tokens were also being used and were stored server-side, could the person that stole the JWT indefinitely exchange this JWT for a new one so long as they keep doing so within the expiry time? Effectively giving them indefinite access to the API?

How is this mitigated against? Short expiry making the chance of renewal within the timeframe less likely?

I’m assuming that in your second paragraph when you mention JWT you’re referring to an access token. Have in mind that neither access tokens nor refresh tokens need to be JWT’s; that’s an implementation detail between the authorization server and resource server for the access token case and just an implementation detail of the authorization server in the refresh token case.

If only the access token is leaked then the attacker will be able to use that access token (assuming it is a bearer token) to call the resource servers (aka API’s) for which the access token is valid and until the access token expires (assuming there is no additional mechanism that support access token revocation/blacklisting).

From the Auth0 perspective and in relation to access tokens currently there is no support for revocation/blacklisting. However, the attacker would still only be able to use it until it expires and is not possible to get a new access token using only another access token (even if valid).

In order to renew an access token, you’ll need the refresh token. In general and in the Auth0 case also, refresh tokens are valid until manually revoked so if your application leaks a refresh token an attacker could be able to use it to obtain access tokens forever or until it would be manually revoked.

In conclusion, the scenario of renewing an access token with just another access token does not apply.

Yes, the JWT assumption was correct, its just an access token.

So in a scenario where the user has an access token and refresh tokens are stored server side (trusted) against the session, the user would require both the access token and the session information in order to refresh? So if the JWT was stored in localStorage and the session info in an HTTP only cookie this would make the chances of getting both of them items tricky? But once you have both of these there is not much you can do except revoke the refresh token if the leak is detected?

The HTTP-only cookie would mean that for example a XSS vulnerability would not be able to compromise the cookie while it would indeed compromise local storage so in that aspect is an improvement, but cookie-based stuff has its own considerations also.

I’m migrating a SPA from v1 API to v2. The mechanism for refreshing the user’s token appears to be quite complicated in v2. Managing a refresh token appears to be problematic, with potential access by an attacker due to the refresh token being available on the client. Plus the need for some kind of scheme to revoke it when a user logs out, closes browser.

I’m not using SSO (or social) nor using the callback URL; rather I’m using the “on authenticate” event. I don’t want to have a hidden iframe to handle the callback, as some of the documentation is suggesting. Am I missing something?

I’m migrating a SPA from v1 API to v2. The mechanism for refreshing the user’s token appears to be quite complicated in v2. Managing a refresh token appears to be problematic, with potential access by an attacker due to the refresh token being available on the client. Plus the need for some kind of scheme to revoke it when a user logs out, closes browser.

I’m not using SSO (or social) nor using the callback URL; rather I’m using the “on authenticate” event. I don’t want to have a hidden iframe to handle the callback, as some of the documentation is suggesting. Am I missing something?

You’re not missing something, the way for the SPA to refresh tokens is to use silent authentication which will generally be used in an iframe so that it can be done in a completely non-disruptive way.