Auth & Security63 min total · 19 parts
OAuth 2.0 and JWT Explained: What Really Happens When You Click "Sign In"
Part 12 of 19 · ~3 min
Access Tokens vs. Refresh Tokens vs. ID Tokens
Tasklight's actual integration juggles three separate token types, and losing track of which one does what is, by a wide margin, where most of the system's bugs actually come from.
| Token | Purpose | Typical lifetime | Goes to |
|---|---|---|---|
| Access token | Proves Tasklight is allowed to call an API; sent on every request | Short — 15 minutes | tasks-service, billing-service, Kestrel's own APIs |
| Refresh token | Gets a new access token without making Priya log in again | Long — days to months; revocable | Only Tasklight's own token-refresh logic, never a resource server directly |
| ID token (OIDC) | Asserts who Priya is — the authentication half | Short; used once, right after login | Tasklight's backend, to build its own session |
Notice what the "Goes to" column is actually implying about who's supposed to read each one. Nothing about tasks-service ever requires Priya's browser to understand the access token it's carrying — that token's job is to sit in a header and be checked by whatever it's handed to, not to be interpreted by the client passing it along. Flip that around for the ID token: its entire purpose is being read, specifically by Tasklight's own backend, the one time it needs an answer to "who exactly just came through that door." Two tokens, built to be consumed in opposite directions — and because both of them are, at the byte level, the same three-part JWT structure from a few chapters ago, it's entirely possible to hand one to the code expecting the other without anything obviously breaking. An access token decoded as though it were proof of Priya's identity, or an ID token forwarded to tasks-service as though it were a valid API credential, are both real mistakes that real integrations ship, precisely because nothing about looking at either token tells you which one you're holding.
It's worth actually following one login through the next twenty minutes of Priya's session, because the reason there are three of these instead of one only really lands when you watch what each one is doing while the other two sit idle.
The moment step 5 of the code flow completes, Tasklight's backend is holding all three at once: an ID token, an access token, and a refresh token, all minted in the same response. It uses the ID token exactly once, right then — decodes it, confirms it's genuinely Priya, and never looks at it again for the rest of the session. It uses the access token constantly: every board load, every task Priya drags between columns, every request to tasks-service carries it. Fifteen minutes in, mid-scroll, that access token expires. Nothing about Priya's experience changes — Tasklight's backend notices the next tasks-service call failed with an expired-token error, quietly uses the refresh token to get a new access token from Kestrel, and retries the request. Priya never sees a login screen, never notices a delay worth mentioning, and has no idea any of this happened. The refresh token itself keeps living on, unused for anything except that one silent exchange, until either it's rotated (next chapter) or Priya's session eventually ends.
Collapse those three into one token and the whole thing gets worse in a specific way. A single long-lived credential good for both API calls and re-authentication would have to be either short-lived — which means Priya re-logging in every fifteen minutes, all day — or long-lived, which means the exact credential riding along on every single tasks-service request is also the one thing worth stealing to stay logged in for months. Splitting the roles apart means the token that travels constantly is the one that expires fast, and the one that's actually dangerous to lose sits still, used rarely, and — as the next chapter covers — can be pulled back the instant it needs to be.