That makes a lot of sense to me! It doesn’t matter how the token is saved right? Would it be best to store these tokens as http-only cookies? From what I’ve learned in this thread this would mean that the tokens are safe from XSS attacks. It would take someone physically using your browser to extract the token, correct?
Sure it does. If you’re storing them in HTTP GET parameters, that’s going to be really easy to pilfer.
But if you’re only considering the limited set of reasonable options, then you probably can’t go wrong with either.
You’re going to want to consider HttpOnly, Secure, and SameSite cookies all in one go.
- HttpOnly: Stops XSS from pilfering cookie values and exfiltrating it somewhere else.
- Secure: Cookies will only be sent over HTTPS; an HTTP connection won’t leak the value to a machine in the middle
- SameSite: Only requests that originate from the same origin will include this cookie, which prevents a lot of CSRF attacks.
I agree that the refresh token is not to prevent them from being stolen, but the refresh token use is very important to mitigate the impact of a stolen token, especially when the tokens are very short lived.
So, when using refresh tokens its recommended to use very short lived tokens. How short depends on your use case. In my apps I prefer to use tokens that cannot be used for more then one request, therefore I refresh them on each API request, and mark the other one as used in the database.
If you refresh the access token by itself, does that mean that the client will have a non-revocable token as long as it can keep making API calls one after another?
If you need the client to present both the access token and the refresh token for each API call, then isn’t the would-be performance gain from using a stateless access token gone?
First, I need to clarify that I am not using OAuth2 on my app, just Phoenix encrypted tokens.
On the first page load a Phoenix encrypted token is generated, and in the next request that comes in with a token correctly signed and not expired is served and that token is marked as used in the database and a new one generated (refresh) and the expire time increases, therefore doing what you say, the ability to keep doing API requests, provided that the user keeps using the application, but the tokens are still revocable. I also include in the token a fingerprinting hash from data present in the request, and on the next time a request comes in the a new hash is calculated and must match.

If you need the client to present both the access token and the refresh token for each API call, then isn’t the would-be performance gain from using a stateless access token gone?
You don’t hold refresh tokens in the client, otherwise you just expose them to be stolen by an attacker.
My approach isn’t about performance gains, it’s about security, and with security comes always some penalties in terms of performance, that the most don’t want to pay now, just later when they got breached, My approach of single use tokens its to make harder to abuse an API, and while it doesn’t make it impossible it puts aside a lot of script kids and easy automation. By other words it requires a lot more skills and efforts from the attacker.
I see. I was assuming one opaque stateful refresh token to get the access token and one stateless access token for the actual API call. Your tokens are always stateful (so you can revoke) and keep changing (so stolen token has a very limited life span)

Your tokens are always stateful (so you can revoke) and keep changing (so stolen token has a very limited life span)
When a stolen token is used by the attacker before the application can use it, then the application is left with a revoked token, therefore not able to see its request served as expected, but for this to occur the attacker needs to be able to craft a request that is identical to a genuine one, otherwise it will fail in the fingerprint check.
what if a legitimate client is trying to make 2 API calls concurrently?
My web apps don’t have that scenario but my next project will have and I have some ideas to tackle that scenario, but not tried them out yet and they aren’t as simple as this one I am now using on my web app.
One of the possible approaches I have in mind is to have a whitelist of API endpoints that can use the same token in a very narrow time window, thus allowing the token to be used in more then one API request, but never more then once for the same API endpoint.
My preferred theoretical approach is that each API endpoint will have it’s own token, therefore the client needs to keep track of which token it needs to use for each API endpoint. On each API response the client receives a new token to use in the next API call to the same endpoint.
Any of my theoretical approaches will require more complexity in both the app and backend, but in the end they will make it harder to use stolen tokens and to spoof API requests. The main problem is TOFU (Trust on First Usage) and for the fact that I still need to provide a static token when shipping the app, unless I build my personal APIs and mobile apps with the SaaS service I work at, that can provide securely a runtime secret just-in-time of being used, and that doesn’t suffer from TOFU.