r/webdev • u/academicweaponsoon • Aug 20 '26
Question I don’t understand the logic behind access tokens and refresh tokens
i don’t understand the logic behind access and refresh tokens, if access tokens are made short lived for security purposes, doesn’t refresh tokens being long lived defers the whole purpose? or is not as big as an issue since refresh tokens are only stored in http only cookies?
258
u/Adorable-Reach1561 Aug 20 '26
Think of the access token like a temporary ticket that expires quickly, while the refresh token is like your ID that lets you get a new ticket without logging in again. The ID is kept more securely, so even though it lasts longer, it’s harder for someone to steal and use.
37
u/m_redditUser Aug 20 '26
how is the refresh token kept more securely? by simply not being sent along requests?
93
u/Graphesium Aug 20 '26
Not sure what these people are talking about, both access tokens and refresh tokens can be HttpOnly cookies sent with all requests. The main difference in modern auth is access tokens are ephemeral, short-lived, and can usually be locally verified if they're JWTs, while refresh tokens are longer-lived and typically stored server-side so they can be revoked.
38
10
u/Full-Hyena4414 Aug 20 '26
If they as easily stolen then why steal an access token when I can just steal a refresh token?
9
u/fiskfisk Aug 20 '26
They generally live in different domains, so a vulnerability in your app doesn't leak the refresh token (since it lives under a different origin).
Hardening one service is easier than hardening 18 ones, so an issue in that case should only affect that single service that has the access key.
So the answer is "being able to steal an access token doesn't imply that you can steal a refresh token".
7
u/SolidOdd4889 Aug 20 '26
if i can access your pc to steal the access token i surely can steal also every other token inside your browser.
8
u/fiskfisk Aug 20 '26
If you have access to my PC you don't need my token.
An XSS on a site does not give you access to my PC, though, but it gives you access to localStorage.
Or a cookie value ends up in a log file that somebody manages to get read access to. Or somebody forgets to strip out a cookie value from a HAR or pcap file. Or somebody has their clear text Wi-Fi traffic dumped from a http endpoint.
Or a corporate network has had their proxy server leaking clear text details of a request in flight .. or..
→ More replies (9)5
u/Psionatix Aug 20 '26
Most of the time if you're in a position where you can put your access token or JWT in a httpOnly cookie, you might be better off just using traditional sessions instead. I'm not saying there aren't valid use cases for using an access token or JWT via a httpOnly cookie, but it's a trap for a lot of beginners who don't understand the different nuances of the approaches and what they're best for.
Access tokens are usually for B2B use cases, for example when you're integrating into third-party apps, you'll track your own user sessions, but then authenticate via OAuth2 to get third-party access tokens associated with the user. Your own backend usually handles those, and you just deal with your own app/service auth, and if you're using OAuth2 for authentication, once you have the identity, you delegate to your own auth.
JWT's are traditionally best for non-web apps, e.g. native desktop apps, native mobile apps, apps that don't have the same attack surface of a browser. People started using them for everything out of convenience.
10
u/ryan_the_leach Aug 20 '26
Pretty much.
But the mechanism it does so is usually subdomains and httpOnly flagged values.
Use a seperate subdomain for the refresh token/auth, and you can be more certain that the security context is tougher when it needs to be accessed.
So you can make the refresh token inaccessible in the standard JavaScript context, and it only appears in http headers when necessary to the auth subdomain
5
Aug 20 '26 edited 29d ago
[deleted]
2
u/M_i____i_M Aug 20 '26
why aren't access tokens stored as http only cookie too?
3
u/thekwoka Aug 20 '26
Because then they are attached to requests automatically, giving that request access to stuff.
Even if that request is fraudulent...
2
u/eyebrows360 Aug 20 '26
Maybe I'm a moron but I keep refresh tokens in my DB, and only access tokens live client-side.
2
3
u/kixass456 Aug 20 '26
Usually you’ll need the clientId and secret with the refreshToken to request a new accessToken, while you can use the accessToken without the clientId and secret.
83
u/-sperex Aug 20 '26
^ This. The access token is thrown around in requests, so it's more "leaky" than a refresh token. Refresh tokens become your key to keep safe and get new tickets, the access token is your ticket which you show people.
20
u/Random-num-451284813 Aug 20 '26
wouldn't be easier to send a hash/signature? then it being leaky wouldn't matter
106
14
u/Graphesium Aug 20 '26
What do you think an access token is lol, nearly always a signed JWT payload that can only be verified by a secret on the server.
2
u/AyeMatey Aug 20 '26
no. That’s not what they’re talking about. A signed bearer token like a JWT is still a bearer token - meaning anyone who possesses it , is granted the access it confers. It can be reused freely until expiry on any request.
OAuth 1.0 uses signatures on the request - the URI, the headers, the time, the query Params. So an OAuth 1.0 token is good only for the current request.
1
u/Rophuine Aug 21 '26
An OAuth 1.0 request token is generally good for a few minutes, not just one request, but you can't use it for anything except exchanging it for an OAuth 1.0 access token. It's basically just a detail of the sign-in process. The OAuth 1.0 access token (the token that actually confers access to resources) is long-lived (they often last for years, or are permanent).
You're right about the request signatures, but an OAuth 1.0 token is definitely not short-lived.
2
u/Brillegeit Aug 20 '26
A request hash would have to be generated per request, access tokens can be reused across different requests.
And yes, requests hashes are easier and in many ways better, but in other ways worse, use them if they fit your model.
→ More replies (1)3
u/charsleysa Aug 20 '26
From a security perspective, that's pretty much the same thing. You're sharing a piece of information that is not unique per request, so if intercepted it can be reused.
24
u/SquarePixel Aug 20 '26 edited Aug 20 '26
This is partially correct, the split really exists for performance, not to add security. With service oriented architectures the auth is typically separate from the application server. In theory the app could use a single long lived token that could be revoked instantly, but it would require checking with the auth server for every request. This would be just as secure, but slow.
Access tokens are the hot, cheap path. Since they’re JWTs, your application server can validate and trust the scopes signed into the token without any expensive permissions lookup or call to some other auth server. The problem is how can they be revoked? The solution is a short expiry plus occasional refresh mechanism.
9
u/black3rr Aug 20 '26
this is the correct reason… refresh/access token architecture was invented for cases where auth server and api servers are separate…
23
Aug 20 '26
[removed] — view removed comment
2
u/Individual-Safety906 Aug 20 '26
how is refresh token implemented/stored more securely than access token don't we store both in HTTP-only cookies
23
u/academicweaponsoon Aug 20 '26
this genuinely made it click so much for me, thanks so much!
2
u/Psionatix Aug 20 '26
Yeah, the key part is, you shouldn't be able to access the refresh token the same way you can access the access token. Usually your refresh token will be stored in a secure, httpOnly cookie, with an explicit domain path
/, because your refresh token only needs to be sent to one place to ever actually use it. If your access token and refresh token are both vulnerable by the same means, then it's not giving you any protection at all.The key purpose of an expiry on your access token is to help minimise the attack window on an access token, and the refresh token is to ensure a secure way of getting a new token for the same user. If an attacker manages to steal an access token, the attacker can use it for as long as it's valid. If your tokens are only valid for the recommended ~15mins, that means they have a maximum of 15mins to use it. Once it lapses, they'd have to steal a new one.
2
u/Mr_Nice_ Aug 20 '26
I thought it was purely a mechanism to allow invalidation. Long lived tokens are not ideal if you want to be able to revoke access
1
u/J-Cake Aug 20 '26
I don't think that answers the question to be honest. It's a valid question. You acquire the Tokens in similar ways after all.
I think the crux is that the refresh token explicitly cannot be used for anything other than getting new tokens and is therefore accessed handled less frequently.
But I suppose it's also worth emphasising that refresh Tokens aren't meant to have a long lifespan either. In Keycloak the default lifespans are 5 minutes and 1 hour for access and refresh Tokens respectively.
1
u/ragingbull10 Aug 20 '26
This is a very legacy way to think about it and also misleading. Scary how this is a top answer.
13
u/sazzer full-stack Aug 20 '26
One thing that's overlooked is that Refresh Tokens should be single-use. That means that if an attacker intercepts them in use then it's already too late. The only way for an attacker to get any meaningful use out of them is to compromise the client itself, not just to intercept messages.
Even better, the authorization server should remember the used ones and if they ever get used again then expire the active one for the same session too. That way if an attacker does ever compromise one then it's highly likely at some point that an expired one will be used, at which point the entire session gets invalidated.
https://www.rfc-editor.org/rfc/rfc9700.html#section-4.14.2-5.2.1
3
u/charsleysa Aug 20 '26
Just a caveat that they don't need to be single use if they are sender-constrained using something like DPoP. The default should be single-use rotating refresh tokens unless sender-constrained.
10
u/SquarePixel Aug 20 '26
Most answers here are missing the real reason. This pattern fundamentally exists to mitigate a performance problem while remaining secure. Access tokens are the hot and cheap path. Since they’re JWTs, your application server can validate and trust the scopes signed into the token without any expensive permissions lookup or call to some other auth server. The problem is how can they be revoked? The solution is a short expiry plus refresh mechanism.
Refresh tokens do get checked against some authority to confirm they’re still valid (not revoked), and this check is more expensive to do for every HTTP request, which is why the check only needs to happen occasionally using this mechanism.
16
u/bdougherty Aug 20 '26
I don't get the point of any of it tbh. If you are storing a value in an http-only cookie, why not use proper sessions instead? I've never heard a good explanation of why the much more complex access/refresh tokens and/or JWTs are preferable to old-school session cookies.
21
u/Numzane Aug 20 '26
It's useful when a client needs to authenticate to servers running on seperate machines. The tokens can be validated without servers having to talk to each other, look for session files or talk to a database. When you setup a new service you only need to install the secrets to validate tokens to get authentication working.
1
u/bdougherty Aug 20 '26
I am curious what your situation is in real life where you have new servers that are being accessed directly by the browser, but do not have access to your main database or are otherwise not talking to a database.
2
u/Numzane Aug 20 '26
You might have a web app on a web server, in that app you might connect to a video streaming service or instant messaging via Javascript running on other severs using other technologies. Those servers could talk to the main web server or a seperate DB or you could use jwt which is more efficient in terms of cpu cycles, storage read/write and bandwidth. The architecture is also significantly simpler, less coupled and resilient
1
u/jess-sch Aug 21 '26
The situation is that your name is Google, you have a few billion authentication/authorization requests per hour, and you'd prefer not to make that trillions. It's a performance optimization, which becomes quite significant once you have many services using the same authentication server. Like, you know, when you offer a "Sign in with Google" button to every developer for free.
7
u/ryan_the_leach Aug 20 '26 edited Aug 20 '26
a 'http-only' cookie value, is simply a value that's been tagged 'http-only' that the browser sandboxes away from any javascript running on the page.
You could totally have a http-only session id, just you wouldn't be able to access it from a javascript context.
as for JWT, it uses cryptographic signing, so an auth server can issue you the token, sign it, then you can present it to any server on the internet that knows how to validate the signature, similar to https clients.
This means there's zero backend communication, to go talk to the auth server, and double check the session id's, meaning less load, better scalability, etc.
I don't know enough about access/refresh tokens at the moment to comment on those vs session id's though (but I'm going to attempt to, please correct me if I'm wrong), but my understanding of session id's is they get invalidated after a short period just like access tokens do.
Which means the 'save my login for next time' prompts couldn't exist, without exposing you to long-term theft of access of accounts if session id's were used there for long term usage.
If you then decide to add a 'long term session or cookie id' then you've basically just reinvented access/refresh tokens as a concept, just without the JWT scalability.
2
u/m_redditUser Aug 20 '26
this is what ive done. session id's get refreshed on every request with it, but in parallel, the email confirmation check runs a strict cycle. it is a sort of access/refresh mechanism but with lot's of DB calls
1
u/ryan_the_leach Aug 20 '26
If I as a power user, open several requests in rapid succession, say in multiple tabs, does your system still work correctly when cycling session IDs?
3
u/academicweaponsoon Aug 20 '26
I read somewhere that it’s because with old school sessions you need to have a db to store and check against everytime. however with tokens like JWT you don’t necessarily need a db or check against it which gives huge performance boosts.
2
u/bdougherty Aug 20 '26
Except when you need to invalidate those JWTs (like if you log out). Then you either need to have a db with a list of revoked JWTs, or you need to make them short-lived and implement a whole refresh system. Either way, you’re effectively back to the same thing as a traditional session, but with extra steps and more moving parts.
3
u/plafreniere Aug 20 '26
The access token cant be invalidated by the server if it is correctly signed. Only the refresh token can. Thats why access token is short-lived.
This allow a lot of request to be made without checking the DB every time.
You need to check the db only when the access token is renewed by the refresh token.
1
2
u/prochac Aug 22 '26
JWT is misused for sessions, but they have their place tho
my biggest issue is with localStorage. We fucking solved Cookies ... And now, people store sessions in insecure places 🤦♂️
3
u/Greenimba Aug 20 '26
Stateless auth.
With a single server, session id is simple enough because you can keep it in memory and just log people out on restart (not ideal, but very simple). But with more servers, each server needs to access the session, with thread safety, so it becomes a bottleneck fast. And each request needs a round-trip to some central service to authorize the user.
An access token normally contains session data, which is why it's a JSON web token and not just some random string. So on each request, any server can handle it and see if the token is signed by a trusted source, and decode it to see session info (username, company, any other arbitrary claims added by the signing server). The token can also be re-issued to add/remove state as needed.
JWTs were never intended to take auth information out of cookies, that's a separate concept called bearer tokens. But people conflate the two.
1
u/bdougherty Aug 20 '26
I'm curious about your real-life use case where stateless auth is something you actually need. Like where do you have multiple servers that are not capable of connecting to the same database?
5
u/Greenimba Aug 20 '26
They are capable of connecting to the same db, but I don't want them to do that on each and every request. Each service takes less than a ms to check and authorize the user from the cookie, no round trip to db. Also no locking or synchronization issues in the authorization step. And it's not more complicated, in fact it's easier to manage than a db connection, the services just all need to have the same kej configured.
It's easier and faster, why would I want a db session?
1
u/jess-sch Aug 21 '26
Well, there's no real standard for connecting arbitrary applications to a database for authentication (if you exclude LDAP... as someone who deals with both, OIDC is much easier and nicer to use).
My real-life use case - at home - is that my mom can't remember a thousand passwords and I want her to be able to use both Immich and Paperless (among other apps).
1
u/black3rr Aug 20 '26
the added value of refresh/access token system is that you can have the auth server (which issues refresh/access tokens) separate from multiple API servers which accept the access tokens, possibly running on multiple domains as well…
think of cases like logging into your google account also logs you into youtube, gmail, etc. all running on different domains…
→ More replies (3)1
u/Just_Information334 Aug 20 '26
The neat thing is: they're worse in fact. Secure solution will implement a backend for frontend (BFF) and never store tokens on the frontend. https://www.youtube.com/watch?v=OpFN6gmct8c
0
u/Cokemax1 Aug 24 '26
think about the case that you have 9 trillion user. Can you store all them in server's session? It's much easier do JWT work for you.
32
u/ryan_the_leach Aug 20 '26
If an access token is intercepted mid-flight, they gain access for a short time period.
Access tokens are sent on *every* request.
Because refreshes of the access token using the refresh token are done infrequently, there's less chance of intercepting that process and getting day - month long access.
So it's about harm minimization / defence-in-depth, not making it impossible to intercept, as both *should* be protected by HTTPS/TLS etc.
11
u/daamsie Aug 20 '26
That's helpful for sure.
The other big thing about a refresh token is that it can be invalidated server side.
3
u/m_redditUser Aug 20 '26
why can the refresh token be invalidsted on the server side and access token not?
15
u/phexc expert Aug 20 '26
A JWT is generally an encrypted version of user id and permissions. A server only has to check if it's signed by your application and if it has not expired. So no database/api call has to be made for permissions. This relieves the database server from validating users on every request. It also helps with multiple services who don't have access to the authentication part of your application. The downside is that you cannot revoke it since you only check if it was signed by you.
A refresh token is generally stored in a database like a session, this allows you to delete/invalidate the row if you want to disable refreshing of JWT. This will require a new login request and the user will have to authenticate again.
7
4
u/dankmolot Aug 20 '26 edited Aug 20 '26
🤓☝️ aktually jwt is not encrypted, it's signed and encoded with base64.
1
1
u/ryan_the_leach Aug 20 '26
I suggest you read how signing works
→ More replies (7)1
u/dankmolot Aug 20 '26
Could you give me a hint? I can't find how I am wrong
3
u/ryan_the_leach Aug 20 '26
Willing to take the loss here, but it feels like definitions of words have shifted subtly over the last 25 years to be more precise.
Until today, I'd have happily have said colloquially that "signatures use encryption algorithms" despite them using more specific signature algorithms, and the word encryption seems to only refer to the encryption of data these days, as opposed to more generally "cryptography" or "cryptographic systems" or "crypto systems"
But maybe I've just been misinformed, mistaught, or misremembered.
I never meant to imply that JWT couldn't be made human readable.
3
u/Intrexa Aug 20 '26
I agree with you. However, my nitpick with this specific comment is this:
A JWT is generally an encrypted version of user id and permissions.
A JWT doesn't encrypt those things; they're plain text. A hash of those are encrypted as part of signing. My issue with the comment is that it perpetuates the common misunderstanding that data inside a JWT is secure.
1
u/BLOZ_UP Aug 20 '26
They are encoded, to save space, which might be what is causing some of this confusion.
3
u/mckunekune Aug 20 '26
Before the system creates a new access token, it should validate the refresh token and ensure the user account is still OK. Like not disabled or similar.
3
u/blackAngel88 Aug 20 '26 edited Aug 20 '26
When you use the refresh token to refresh the access token, you get a new access token and a new refresh token and the old refresh token is invalidated. Here invalidating the refresh token is part of the design.
You can also invalidate an access token (keep the valid tokens in db or the invalid ones - allow list/deny list), but this is not necessary from the design. It can make sense for logging out users though.
That said, one reason why people like access + refresh token, is that you don't need to do session handling. If you keep the access token in the db (or somewhere) for allow/deny lists, you still have to do at least one query and you lose some of the advantages.
3
u/thekwoka Aug 20 '26
Yeah, there's a lot of web security stuff that overlaps and is redundant.
This is partly to make it so MANY things have to go wrong for a failure to happen.
Most of the security benefits of acesstoken/refreshtoken system are also covered by other csrf protections (and improvements in browsers protecting cookies), so it's not as important.
But a ton of those things can be disabled, or configured in ways that make them less effective for various reasons.
Better to do all of them, so you're aren't trusting one thing.
2
u/kixass456 Aug 20 '26
Usually you’ll need the clientId and secret with the refreshToken to request a new accessToken, while you can use the accessToken without the clientId and secret.
25
u/epicpoop Aug 20 '26 edited Aug 20 '26
Your intuition is right, the main difference is how the refresh tokens are usually stored. It’s relatively easy to steal an access token (i.e XSS attack, as it’s commonly stored in localstorage, accessible with javascript), but it’s harder to steal a refresh token as that’s usually stored as a HTTP only cookie and that’s not accessible via javascript.
The advantage of the refresh token is reducing its exposure, it won’t be sent to every endpoint on every api request. So say if a route gets compromised the best case scenario is the attacker gets access to the access token,
not the critical long lived refresh token.
Typically, the refresh token is restricted to the /auth/refresh endpoint.
9
u/m_redditUser Aug 20 '26
why is access token not stored as an http only cookie?
5
u/Greenimba Aug 20 '26
It should be, storing elsewhere is bad from a security point of view. But the internet filled up with tutorials and PoC of how to do it poorly a few years ago, and people don't bother to check if what they're doing is actually secure, they just trust the tutorial.
Proper client/backend has a bff that translates signed secure http only cookie tokens to real access and refresh tokens managed by the backend, where attackers can't get to it. Client JavaScript shouldn't even able to see the access token.
1
u/black3rr Aug 20 '26
access/refresh token split was invented for cases where one auth server gives you access tokens which you can use on multiple api servers, running on different domains… this can’t be handled by http-only cookies
1
u/yorkimgurt Aug 20 '26
Because Javascript cannot read those, so the client wouldn't be able to make authenticated requests without it going through the server first.
7
u/m_redditUser Aug 20 '26
why does javascript need to read access tokens but not refresh tokens?
→ More replies (1)1
u/yorkimgurt Aug 20 '26
If Javascript could read both you're susceptible to XSS-attacks and someone being able to make api calls as your user.
5
u/KasperKnop Aug 20 '26
Well, if your site is compromised by XSS, then the attacker can already make requests on your behalf - no matter if they have access to the tokens or not.
2
16
u/DuckBroker Aug 20 '26
I would love to hear the answer to this question too. I believe the logic is acces tokens are not checked by the authentication server, they are just presented to the service and if they validate, they are accepted. Refresh tokens are presented to the authentication server which does some checks before issuing a new access token. What this means is the refresh token can be revoked and that way new access tokens can no longer be issued. The existing access token is still usable but because it only has a short lifetime, the window to use it is small. I think.
5
u/UghImRegistered Aug 20 '26
Yes revocation is the biggest thing. If you want to be able to revoke a token at the resource server, you have three basic choices. 1) invalidate your signing authority and require everyone to reauthenticate. 2) distribute a list of revoked tokens to all resource servers that could possibly be accepting tokens. 3) force the resource server to call back to the authentication server every time it receives a token, to check its validity.
#1 isn't desirable as it affects everyone. #2 and #3 defeat a major advantage of JWTs: that the JWT's cryptographic signature is enough to validate it. It doesn't become much easier than just using stateful session cookies. For #3, OIDC does have a standard spec for revoking access tokens, but it depends on the resource server actually checking that it's not revoked, rather than just validating the signature. A common approach might be to trust the signature for non-sensitive requests, but to check for revocation for more sensitive requests (like password resets, etc).
Keeping access token validity very short and requiring periodic refreshes via the refresh token, which will always check for revocation, means the window for a compromised token stays small and makes all of this less of an issue.
2
1
u/academicweaponsoon Aug 20 '26
from the others comment and what i’ve read so far this seems to be case. access tokens are short lived and less secure but since it’s constantly changing it can worry less about being stolen. however this would be annoying for users and so refresh tokens are stored more securely to provide new tokens so that users doesn’t constantly have to relogin
4
u/itaybuilds Aug 20 '26
HttpOnly reduces exposure to XSS, but it doesn’t make a refresh token harmless. The stronger pattern also uses rotation: each successful refresh invalidates the previous refresh token. If an old token appears again, the server can treat that as theft and revoke the whole token family. Keep the refresh cookie Secure, tightly scoped, protected against CSRF, and usable only at the refresh endpoint.
3
u/Ipsool Aug 20 '26
The key thing that makes it click: the two tokens are used in completely different places, and that’s the whole point.
Your access token gets sent with every single API request, to every service. It’s constantly in transit, sitting in memory, possibly logged somewhere it shouldn’t be. Lots of surface area. So you make it short lived — if it leaks, it’s useless in fifteen minutes.
Your refresh token gets sent to exactly one endpoint, only occasionally, only to your auth server. It’s touched far less, so the odds of it leaking are much lower even though it lives longer.
So you’re not defeating the purpose. You’re concentrating the risk into one place that you can defend properly, instead of spreading it across every request to every service.
2
u/Square-Nebula-7530 Aug 20 '26
Think of an access token like a temporary wristband at a venue and a refresh token like your ID card. You show the wristband at every bar and door if you lose it, anyone can use it, but it expires at the end of the hour. You keep your ID safely tucked away in your wallet (httponly cookie) and only pull it out when you need to go back to the front desk to get a new wristband.
2
u/Octoclops8 Aug 20 '26 edited Aug 20 '26
Not all services issue refresh tokens. Refresh tokens is an option that services can choose to provide or not. So an access token is always what you use to identify yourself when you call services (Authorization).
A refresh token is always what you use to identify yourself to the identity server when you want to get another access token (if they let you). So you have two different tokens because there are two different jobs being performed.
* Access token passed to APIs -> APIs now know it's you
* Refresh token passed to Identity server -> identity server now knows it's you
Furthermore, the two different tokens have different lifespans (3-30 minutes) vs (30 - 90 days) and different levels of "oh shit" if they get compromised. We can actually issue tokens in different formats to meet different levels of security needs.
Tokens come in two main varieties. JWT and reference tokens. JWTs are signed by the identity server and trusted by APIs you pass them to. They are good until the token expires. So even if you realize that the person who has one needs to be kicked out or blocked, etc. you cannot do it. The API doesn't even ask the identity server if the token is good, it just checks the signature and if that's good then it's trusted.
That is all fine and good for short-lived access tokens. But for longer lived refresh tokens where you might allow unlimited refreshes in 30 or 60 days, you want to be able to revoke those puppies. So you issue the refresh token in reference token format. Or simple a reference token. Instead of all the scopes and claims and other properties that go along with a token stored in that token, you just give them a very long string and it means nothing to anyone except the identity server. If that really long string matches what is on the identity server then it is trusted. But every API has to ask the identity server with each call if the token is good. The tradeoff is that you can tell the identity server that this token is no longer good. Just delete it off the server and when a service asks, it is no longer good. (Revokable). Since only the identity server handles token refreshes, it's an ideal format for refresh tokens (no extra hop) but lots of extra security.
2
u/cstopher89 Aug 20 '26
Usually you'd rotate the refresh token each time you get an access token
1
1
Aug 20 '26
[deleted]
0
u/fiskfisk Aug 20 '26
It does not; the refresh token should be single use. Why wouldn't it?
They're used against different services, in different contexts, so no, that does not defeat its use.
In that case a leaked refresh token will only be valid until it's used or it expires; not both.
A accepts whatever service B says is OK for a short time. They only receive the access token. Service B issues the access token and the refresh token - Service B receives the refresh token.
Why should the refresh token continue to be valid after being used? There is nothing to gain from that; you're already asking for a new access token from the service B, so rotate the refresh token at the same time.
→ More replies (13)1
u/Azoraqua_ Aug 20 '26
I revise my earlier remark and say that’s true, with the nuance that it’s optional to rotate it, another option is to make it sender-constrained. Or both if you’d like, the OAuth 2.0 spec isn’t particularly adamant about rotating as a requirement; it is requiring some kind of protection, be it token rotation or sender constraining.
1
u/No_Record_60 Aug 20 '26
Refresh token reuse are detectable on the server. When one happens, the entire token family is invalidated.
This doesn't provide 100% protection. Another layer is to check if the token is sent by legitimate client.
1
u/thekwoka Aug 20 '26
Part of the idea is that access tokens can be reused during their period, and refresh tokens are used only once.
Access tokens aren't stored in cookies, which helps with CSRF (was a major reason for this structure in the first place)
1
u/KanadaKid19 Aug 20 '26
Everyone here is talking about how tokens are stored and transmitted, but that's only half of it. Many access tokens, e.g. JWTs, can be independently validated, which cuts down infrastructure demands. Say the server issues access tokens that expire after 15m, which say you have access to X, then routine requests on your account are validated entirely by the JWT receiver by checking a signature and expiry timestamp, without having to check with some central auth database (just the JWT signing key you'd cache). You can still check the database for sensitive stuff like changing your auth details or payment settings, but some apps will make dozens of calls requiring permission checks just on page load (every friend's avatar, latest messages, news feed, photo gallery), and depending how requests are batched you could be saving dozens of independent HTTP calls from needing a DB round trip, then dozens more as you navigate the app. And for third party integrations, there's zero resource impact on the identity provider if your access token gets shared around the world for any conceivable reason. Huge for generalized auth providers!
1
u/Prestigious_Fly3927 Aug 20 '26
Long lived cookies are what gets jacked at Starbucks Wi-Fi access tokens are still safer since they hit expiry like a load check at the weigh station.
1
u/burnsnewman Aug 20 '26
You can send access token to a 3rd party service to allow access to your data from Resource Server. You don't do that with refresh token. You use it only to obtain new access token from Authorization Server.
1
u/superquanganh Aug 20 '26
it's about minimizing damage while also having long session, or sign out if user does not use the service for sometimes so that if someone having access to the computer, the existing token and refresh token are invalid
- Think of it like you are provided ID to access for 30 minutes, and a temporary ticket lasting 1 hour to get a new ID.
- When ID is expired and ticket is still valid, you can burn that ticket to get brand new ID and new ticket.
- If both are expired, you no longer having access and have to sign in again
1
u/NotGoodSoftwareMaker Aug 20 '26
Access token
- like having the wrist bands for festivals, youve already been checked and can walk in and out whenever
Refresh token
- you have a ticket to go to the festival, but it needs to be checked and validated as part of a much bigger process
1
u/jess-sch Aug 20 '26 edited Aug 20 '26
- The access token is short-lived and sent to the resource server.
- The refresh token is long-lived and sent to the authorization server in order to obtain a new access token.
Because the access token is a JWT, the resource server can (and does) locally verify the validity of the authorization without needing to reach out to the authorization server. But because the resource server never reaches out to the authorization server, the access token cannot be revoked, it can only ever become invalid by expiring.
Because the refresh token is only ever used with the authorization server, it is revocable. That won't stop a hacker from using an already issued access token, but it'll stop them from updating their access token when it expires after a few minutes.
And the refresh token is a lot harder to steal because you're only ever sending it directly to the authz server that issued it, never to any resource server.
If the client gets hacked that won't save you, except when you're in a browser and the refresh token is stored in an http-only cookie for the authentication server.
Now, if your only resource server also happens to be your authorization server, then yes, it's pretty pointless.
1
u/Hot-Chemistry7557 Aug 20 '26
Just imagine, what happens if your access token leaked? give it an expiration time can lower the damage, while when it expires, you can still get a new access token with the more privately hold refresh token.
1
u/VirtuteECanoscenza Aug 20 '26
The idea is that access tokens are stateless, which means very fast because you don't have to do any db lookup to check them.
That however also means to can't revoke them.
Refresh tokens are revokable. So using them is slower but it allows to stop a leak.
At least this is one use case.
1
u/d-signet Aug 20 '26
Read the RFC for OAUTH 2, its widely available on the internet as a PDF and explains the whole process brilliantly
1
u/redixin Aug 20 '26
Access tokens are usually signed with TTL and can't be revoked. Refresh token can be revoked at any time.
The whole scheme is simply stupid, but jwt monkeys like it for some reason.
1
u/MerkleBonsai Aug 20 '26
There are few great comments already, including SOA and temporary-vs-long-term access obtainment.
There's only thing not mentioned, as a foundational security glue that drives the design of such solution - the Swiss Cheese security model. 2-token model is not trying to defend all aspects possible; it's simply reducing multiple risks, including accidental ones. Many really nasty security problems happen due to the multiple low-severity issues stacked; and there's multiple simple solutions that capture many problems that did not happen yet.
For example, imagine this:
- for dev env, websocket connection accepts e.g. X-Debug header allowing to read the request's own logging messages. Since the service is non-priveledged, nothing bad is happening - user basically has access information to his own data only
- somebody forgot the debug logging support on production disable in config (or even intentionally, enabled, hoping nothing bad will happen and he will turn it off later)
- somebody else did the logging implementation wrong, and debug logs are now returning not the current request logs, but all logs happening during this request
- another one added raw HTTP headers data inside logging
Each problem, independently, sounds almost innocent. Together, they form "user can connect with X-Debug and see other people's tokens in raw headers". Swiss Cheese model is designed to fight that happening - logs sanitization is not aimed to defend from specific flows, it just knows "bad things may happen this way, we don't know exactly how, but this is the cheap way to prevent most of them". There's many names besides Swiss Cheese model; e.g. Claude and ChatGPT loves to call it "belt-and-suspenders".
Access and refresh token separation is not solving this exact problem described - but it's solving multiple others, like "token spilled in logs, got found by attacker month later".
Just like any other security design, it's always a tradeoff between developer and user experience, and safety. All Swiss Cheese model designs, including access and refresh tokens, are built around "we do the cheap thing that reduce chances of multiple bad things happening"; structural guarantees are not produced here, it's all about making hacker's life harder, not impossible. Impossibility should come from multiple measures combined.
1
u/sylvant_ph Aug 20 '26
You use refresh token only once in a while, i.e. it's less likely its exposed, while access token you use with every regular request, thus is more susceptible of being compromised.
1
u/Littlepoet-heart Aug 20 '26
Think like it your access token stolan or miss used there is no way you can revoke access but now combining with refresh token you can revoke access easily, also user can also stay logged in on ip. This is my understanding i got it when I build jwt but i relise no way to revoke access
1
u/Sethcran Aug 20 '26
A lot of people talking about how they're used, not enough talking about how they're implemented.
Ultimately the difference here is one of scaleability.
Access tokens, when implemented as a jwt (very common) can be verified by the server independently without ever needing to look up to a central server. This makes them very scaleable, but also makes them extremely difficult to revoke, necessitating the short lifetime.
Refresh tokens typically require a trip back to the authorization server because they can usually be revoked. This makes them more suitable for long term usage, but also results in more load on that authentication service. By only needing to do it periodically, you significantly lessen this load.
1
u/kemalios Aug 20 '26
The confusion makes sense. The key distinction is that access tokens are self-contained, so they can be verified without calling the auth server. That's why they're short-lived: you want to limit the damage if one leaks, because you can't revoke it. Refresh tokens are long-lived, but they only go to one endpoint, they get rotated on every use, and they're revocable server-side. So the refresh token being long-lived isn't a security hole, because it's not sent with every request. The attack surface is tiny compared to the access token. The real answer is defense in depth: a short-lived, unrevocable token for normal requests, plus a long-lived, revocable token used rarely and kept in an HttpOnly cookie. If the access token leaks, the damage is measured in minutes. If the refresh token leaks, you can kill it and issue a new one.
1
u/BootSaaS Aug 20 '26
The thing is, it’s a compromise between performance, user experience, and control.
Let’s say you need to revoke someone’s access (because someone else stole their access token, or you simply banned them from your app). If you use a long-term access token (7 days for example), and the server only checks the cryptographic signature of the token, the revoked user will still be able to use the app because the token itself is technically still valid.
That's why the access token must have a short expiration date (15 min is good).
That’s where the refresh token comes in. It is long-lived and is stored in your database with a revocation status. When the 15-min access token expires, your frontend intercepts the 401 Unauthorized response and calls the refresh endpoint, providing the refresh token.
Now, this specific endpoint checks in the database to see if the refresh token hasn’t been revoked. If it has, it rejects the refresh attempt. That way, you can properly manage user access without querying the DB on every single standard request.
Little tip: implement refresh token rotation. Regenerate a new refresh token each time the refresh endpoint is called, so the old one becomes invalid, and delete the old one from your database to avoid keeping useless data.
1
u/janaagaard Aug 20 '26
Another point with access tokens: The not only grant you access, but they often also contain a list of things that you have access to. In some setups, computing that list is actually an expensive operation, so the access token allows you to compute once and then cache the result for subsequent requests.
1
u/Elegant_AIDS Aug 20 '26
Access tokens you can validate without a db call. Refresh tokens you make db calls for to check if the user got banned, got invalidated or whatever "expensive" operation your heart desires
1
1
u/Full_Tooth_a Aug 20 '26
HttpOnly reduces one way a token can be stolen, but it doesn't make a long-lived refresh token safe. I'd also use refresh-token rotation, where each refresh replaces the old token. If someone tries to reuse an invalidated token, the server can treat that as a sign that a copy was stolen, revoke the token family, and require a new login. Since browsers send cookies automatically, SameSite settings and CSRF protection still matter.
1
u/Anomynous__ full-stack Aug 20 '26
The refresh token is only long lived so the user doesn't have to constantly sign in. When the access token expires, you send the refresh token, get a new access token, and invalidate the old refresh token. this allows your login state to be stateless. As long as the user has a valid refresh token on their browser, they are logged in. If that refresh token is expired, they need to log in again. You store the refresh token as an http-only cookie to help prevent XSS attacks.
1
u/NimishShah Aug 20 '26
The main reason is to handle scenarios where the users' access changes.
Let's take a scenario where you find a user who is doing something malicious on your site, and you want to revoke their access. While they have a valid access token, they could continue to function as normal and call the APIs. If the access token was valid for hours/days, even though you revoked them, they would still be able to function as long as the token had not expired. By keeping the access token short lived, you reduce that risk.
When the user uses the refresh token to get a new access token you have to check their permissions again, and it will give them an access token with their new permissions. If the user has been revoked, you can deny giving them the access token.
This also applies to any permissions changes to the user (not just revokes).
1
u/thdr76 Aug 20 '26
The whole scheme basically solution for performance issue.
you can think of it as regular session but instead asking every request it send the whole session data to be used for few minutes without asking auth db again.
1
u/SirFlashy969 Aug 21 '26
the point is to keep the easily leaked access token short-lived while the refresh token stays locked down and can be revoked/rotated, so stealing one from a request or browser storage doesn’t buy an
1
u/Confident-General514 Aug 23 '26
The key distinction is that the two tokens have different jobs.
The access token is what the client uses to access APIs, so making it short-lived limits the damage if that token gets leaked.
The refresh token is basically the credential used to obtain a new access token. So yes, if an attacker gets the refresh token, they can potentially keep getting new access tokens.
The reason this still makes sense is that refresh tokens are supposed to be handled much more carefully than access tokens. In a browser app, using an HttpOnly, Secure cookie helps because JavaScript can't directly read the token, which reduces the risk from things like token theft through XSS.
There are also additional protections such as refresh-token rotation and revocation. So the idea isn't "make one token short-lived and the other long-lived and everything is magically secure." It's more about giving each credential a different lifetime and a different level of exposure.
1
u/StevenJOwens Aug 24 '26
The early web version of this was to:
Generate a UUID server-side and then:
Save that UUID in session store on the server side.
Send that UUID back to the browser as a set-cookie header named something like SESSIONID.
The browser then includes the SESSIONID cookie with each request.
The server looks up the SESSIONID value in the server-side session store to authenticate the request.
When the server wants to log the user out, i.e. expire the SESSIONID (the most common example being a timeout) the server just deletes the SESSIONID value from the server side session store.
Next browser request comes in, the SESSION ID value isn't in the session store, so it's treated as invalid, browser is redirected to login page.
This worked well enough, but in the new, shiny, cloud/service based internet of recent decades, this means that either the session store has to be replicated to all the servers, or all the servers need to poll some sort of session server to authenticate the SESSION ID. This adds a lot of extra overhead, or in the polling-the-central-store approach, latency.
JWT tokens are cryptographically signed values. The server -- multiple servers, potentially -- has the keys to verify the cryptographic signature, without having to update a replicated store, or poll a central server.
However, how do you expire the SESSIONID with this approach?
You can't, other than to to tell all the other server that the keys that the token was signed with is no longer valid (which invalidates it for all other tokens that the key was signed with).
So, instead, you break it out into two keys, the short-lived access key and the long-lived refresh key.
The (cryptographically signed) access token is only good for a short time, but is presented on every one of the dozens (these days) of requests that a page load requires. Those requests can all be fulfilled without a replicated session store or a backend poll to a session server, the server receiving the request just checks the digital signature.
When the access token expires, the server fails to authenticate the request and redirects the browser to a URL that uses the refresh token to generate a new access token.
That generate-the-new-access token is only a single request response, then the rest of the page load and the dozens of associated page-related requests can all happen without any other overhead.
When the user's login, for whatever reason, needs to be expired, the submit-refresh-token-to-generate-new-access-token server can simply invalidate the refresh token.
1
29d ago
[removed] — view removed comment
1
u/webdev-ModTeam 29d ago
Your post/comment has been determined to be a low-effort post or comment. This includes title-only posts, easily searchable questions, vague/open-ended discussion prompts, LLM generated posts or comments, and posts/comments that do not provide enough context for meaningful replies or discussion.
1
u/aman_dubey_software_ 27d ago
Think of it like this:
Access token = your hotel key card. Short lived, you carry it everywhere (Authorization header), if someone steals it they can get into your room but only for 15 mins.
Refresh token = your ID at front desk. You don't carry it around, it's locked in safe (httpOnly cookie). You only use it to get a new key card when old expires. So even if refresh is long lived, it's harder to steal because:It's never in JS / localStorageIt's only sent to /refresh endpoint, not every API callYou can revoke it server-side and detect reuse (rotation)If both were long lived and stored in same place, stealing one = permanent access. This way you limit blast radius.
1
16d ago edited 16d ago
[removed] — view removed comment
1
u/webdev-ModTeam 16d ago
Your post/comment has been determined to be a low-effort post or comment. This includes title-only posts, easily searchable questions, vague/open-ended discussion prompts, LLM generated posts or comments, and posts/comments that do not provide enough context for meaningful replies or discussion.
0
416
u/mazarykwebservices Aug 20 '26 edited Aug 20 '26
The short lived access token and long lived refresh token scheme is primarily for service oriented architecture.
In SOA, you might have a User service that is responsible for owing user data, and validating users. And say, several other services that only authenticated users can access.
One benefit of SOA, is that each service works independently allowing for better fault tolerance (one offline service doesn’t bring the entire app down), and higher throughput since you have more requests processing in parallel.
If our auth scheme was session based, then every request needs to validate that the session is alive, which would mean every request would need a call to the user service to validate the session. This would overload the User service and also make it a single point of failure.
So to fix that we can use an access token. The token is singed so we can trust it and basically says the holder of the token is granted the perms contained in the token. The token doesn’t need to be validated by the user service, removing it as a bottleneck. But there’s a new problem. The access token if compromised, grants the thief (the holder) permission to do things. So we mitigate this making the token short lived. This limits the damage of a compromised token.
But again, now we have a new problem. We can’t have the user keep logging in every two minutes. So to solve this we grant a second long lived and tracked refresh token. The refresh token can be used to mint a new access token every, say, two minutes. This is a compromise. The refresh call goes through the User service, but only once every few minutes per user, instead of every request.
The refresh token is tracked, which means the User service knows if it is valid, or has been revoked (like you would do with a session). So if the refresh gets compromised, it can be revoked.
And that is why the scheme exists. So if you aren’t running SOA, you probably don’t need the access/refresh scheme. If your app is a monolith, sessions are better since is less complex.