I've been scrolling through StackOverflow and other resources to get an exact guide on how to implement refresh tokens into the JWT flow. Almost all give a brief explanation on the differences between access and refresh tokens and why they are both needed, however, they often come to a solution that often just adds extra steps onto simply using an access token.
My understanding is, the whole point of JWT's are to be stateless, meaning we do not need to use alternate solutions such as sessions which requires retriving and updating items in a database. JWT's allow us to send an encrypted token, created server side, that can be decoded by anyone, however, only we can verify that we actually created such a token. The main flaw with this approach is that if an attacker gains access to a users token, they will have unlimited access that cannot be revoked servers side and is a critical flaw that needs to be addressed. This is where refresh tokens come into play and is where I believe a lot of confusion and misinformation occur.
Access tokens are short lived tokens that are used to directly access protected resources. This results in great security as an attacker has a limited amount of time to retrieve protected resources, however, it leads to terrible UX. Refresh tokens are introduced to balance UX and security, as long as we have a valid refresh token, we can refresh our access token.
Here are the problems I need help addressing:
The main point I see being spoken about when deciding to use refresh tokens is the ability to revoke access and invalidate a refresh token, stopping everyone with that refresh token from retrieving an access token. I dont believe this can possibly be done statelessly so we need to store these refresh tokens in our database. Do we create a table specifically for all our refresh tokens for every user? Do have one refresh token for each user?
What if I, as an attacker, retrieve your refresh token, all I need do is send a request to refresh / obtain an access token and keep doing this each time the new access token is expired and have unlimited access to your protected resources. To expand, whats stopping me from going onto someone elses computer and copying the refresh token onto my computer and doing exactly what I just mentioned. I can understand not taking this into account as its almost identical to someone writting their password next to their computer.
I can see this being very secure and have seen it mentioned in other posts, however, how is it possible without the need of a database. Once we issue a refresh token, we cannot edit that same token without issuing a new token or storing it in a database and editing it there.
I havent seen this discussed anywhere so it leads me to believe there is a flaw im not seeing. When a user logs in, they a granted a refresh and access token, that refresh token contains an ID that is stored on a users record. When we refresh an access token with a refresh token we check if that ID stored in our database and in the refresh token match. If they do, a new access token is granted if not, the request is rejected. This should allow us to invalidate every refresh token issued prior by changing that unqiue ID whilst allowing new refresh tokens with the new ID to be valid.
I think a lot of what im getting at is, is it impossible to incorporate refresh token statelessly. Also, if you know of an exact guide as to implementing refresh tokens using JWT, dont hesitate to drop the link.
Edit: For anyone looking, this video helped explai the flow. I will keep the post open however, as there are still some questions not answered from it.
One can argue that a JWT is a stateful object. It contains claims that are true for a certain period of time.
Also, if the JWT is encrypted, then it shouldn't be the case that anyone can decode it. That defeats the whole point of encryption: to make the data unreadable to everyone but those with a particular secret to decrypt it. What you mean is that it contains a signature. That signature ensures that the contents haven't been tampered with. And anyone can do this that has the public key, which is assumed to be true since it is public.
Access tokens aren't necessarily short-lived, but as you've pointed out, it provides better security. So, in general, it's better to keep the lifetime short.
But, it would be pretty annoying to users if they had to repeatedly log in whenever the short lifetime is up. That's where refresh tokens come in.
The client can get a new access token without having the user re-authenticate and re-authorize access.
- How is it possible to revoke a refresh token using JWT's?
The authorization server can mark a particular token as corrupted or invalid. When the client requests a new access token, the authorization server will check to see if that particular token has been marked as invalid, and, if it has, the request will be denied.
- Whats stopping an attacker from retrieving an refresh token and using it like an access token?
TLS and client authentication. First, the communication between the client and authorization server should be done over TLS so that it's encrypted. Second, when the authorization server gets a request to issue a new access token and is presented a refresh token, the authorization server is supposed to authenticate the client submitting the request.
If the client type is confidential or the client was issued client credentials (or assigned other authentication requirements), the client MUST authenticate with the authorization server as described in Section 3.2.1.
RFC6749-Sec.6
- Should refresh tokens be a one time use?
Strictly speaking, the original spec did not require it to be one-time use. However, yes, the refresh token should be invalidated after it's been used for the best security. The authorization server can issue a new refresh token when a previously issued one is used. This shrinks the attack vector similar in concept to shortening the access token lifetime. But, it doesn't stop a malicious actor from using the refresh token before the real client.
The authorization server MAY issue a new refresh token, in which case the client MUST discard the old refresh token and replace it with the new refresh token. The authorization server MAY revoke the old refresh token after issuing a new refresh token to the client. If a new refresh token is issued, the refresh token scope MUST be identical to that of the refresh token included by the client in the request
RFC6749-Sec.6
- Can I store a unique ID in a database which is then added to a refresh token?
The original spec doesn't specify how it must be done, but it does provide an example of how to detect refresh token abuse:
The authorization server MUST verify the binding between the refresh token and client identity whenever the client identity can be authenticated. When client authentication is not possible, the authorization server SHOULD deploy other means to detect refresh token abuse.
For example, the authorization server could employ refresh token rotation in which a new refresh token is issued with every access token refresh response. The previous refresh token is invalidated but retained by the authorization server. If a refresh token is compromised and subsequently used by both the attacker and the legitimate client, one of them will present an invalidated refresh token, which will inform the authorization server of the breach.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With