Demystifying the Tokens

Every time you hit "Sign in with Google" or log into an app, a handful of tokens fly between systems. Here's what each one does - no fluff. --- ## Authorization Code - The Handshake This is the shortest-lived token in the pipeline. It's not a token you keep - it's a one-time handshake that proves the user just authorized your app. ``` Browser → Authorization Server → gets a code Browser → Your App → passes the code Your App → Authorization Server → exchanges the code for real tokens ``` - **Lifetime:** Seconds to minutes - **Format:** Opaque string (a random blob) - **What it does:** Proves the user authorized your app. Exchange it once, then it's dead. - **Why it exists:** So the browser never sees the actual tokens. The code goes through the frontend, but the real tokens come back over a secure backend channel. --- ## Access Token - The Key Card This is the token your app sends to APIs. Think of it like a hotel key card - it opens specific doors for a limited time. ```http GET /api/orders HTTP/1.1 Host: api.example.com Authorization: Bearer eyJhbGciOiJSUzI1NiIs... ``` - **Lifetime:** Minutes to hours (typically 15 min - 1 hour) - **Format:** JWT or opaque string - **What it does:** Proves to the API that your app has permission to access specific data - **Who reads it:** The API (resource server). Your app just passes it along. - **Contents:** Scopes, permissions, maybe a user ID - but never PII **Key rule:** The access token tells the API *what you can do*, not *who you are*. If you need identity info, use the ID token. --- ## ID Token - The Photo ID This one only shows up in OpenID Connect flows. It's proof of identity - a JWT signed by the identity provider that says "this user just logged in." ```json { "iss": "https://auth.example.com/", "sub": "248289761001", "name": "Jane Doe", "email": "jane@example.com", "exp": 1704070800 } ``` - **Lifetime:** Minutes to hours - **Format:** Always a JWT (so your app can verify the signature) - **What it does:** Tells your app who the user is - name, email, avatar, etc. - **Who reads it:** Your app (frontend). Never send this to an API. - **Validation:** Your app must verify the signature using the provider's JWKS keys **ID token vs. access token:** The ID token is for *your app* to know the user. The access token is for *APIs* to authorize requests. They serve completely different jobs. --- ## Refresh Token - The Re-Entry Pass Access tokens expire quickly by design. The refresh token lets your app get new access tokens without bothering the user to log in again. ``` Your App → Authorization Server → POST /token with refresh_token Authorization Server → Your App → new access token (+ maybe new refresh token) ``` - **Lifetime:** Days to months (long-lived) - **Format:** Opaque string - **What it does:** Gets you new access tokens silently in the background - **Storage:** Must be stored securely (server-side, httpOnly cookie, or secure storage) - **Rotation:** Best practice is to issue a new refresh token each time one is used (so old ones can't be replayed) **When to use it:** After the user logs in the first time, all subsequent visits should use the refresh token to get fresh access tokens. The user never sees a login screen again until the refresh token itself expires or is revoked. --- ## The Big Picture Here's how they flow together in a standard OIDC Authorization Code flow with PKCE:
User / Browser clicks "Sign In" authorize Authorization Server authenticates the user code Your Backend exchanges the code tokens ID Token who you are Access Token calls APIs Refresh Token gets new tokens
The **ID token** stays in your app to identify the user. The **access token** goes to your APIs to authorize requests. The **refresh token** is held securely by your backend and used later to mint fresh access tokens when they expire — so the user never sees another login screen until the refresh token itself expires. ## One-Liner Summary | Token | Purpose | Lifetime | Read by | |-------|---------|----------|---------| | Authorization Code | One-time handshake | Seconds | Auth server only | | Access Token | Calls APIs | Minutes–hours | The API | | ID Token | Identifies the user | Minutes–hours | Your app | | Refresh Token | Gets new tokens | Days–months | Auth server only |