Every OAuth flow ends in JSON
Whatever OAuth 2.0 flow you use — authorization code, client credentials, refresh — it ends the same way: the authorization server returns a JSON token response. Understanding that small object is the key to integrating any "Sign in with…" button or third-party API.
{
"access_token": "eyJhbGciOiJIUzI1NiI...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "def502004f...",
"scope": "read write"
}The fields, decoded
| Field | Meaning |
|---|---|
access_token | The credential you send to access protected resources. |
token_type | Almost always Bearer — how to send the token. |
expires_in | Seconds until the access token expires (e.g. 3600 = 1 hour). |
refresh_token | Used to get a new access token without re-login. |
scope | The permissions actually granted (may differ from requested). |
Using the access token
You send the access token in the Authorization header as a Bearer token:
GET /api/me
Authorization: Bearer eyJhbGciOiJIUzI1NiI...The resource server validates it and returns your data — or 401 if it is missing, expired, or invalid.
Refreshing without re-login
Access tokens are deliberately short-lived. When one expires, exchange the refresh token for a fresh access token:
POST /oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&refresh_token=def502004f...The response is another token JSON object — often with a new refresh token too (rotation). The pattern: a short-lived access token for requests, a long-lived refresh token kept safe to mint new ones.
access_token vs id_token
OpenID Connect (login) adds an `id_token` to the response. Do not confuse the two:
- `access_token` — for calling APIs ("what this app can do"). Treat it as opaque; do not parse it for identity.
- `id_token` — a JWT about the user ("who logged in"). Decode it to get the user's profile claims.
Using an access token where an id token belongs (or vice versa) is a common security mistake.
Handling tokens securely
- Never log tokens — redact
access_tokenandrefresh_tokenin any JSON you log. - Store them securely. On the server, encrypted at rest. In the browser, prefer httpOnly cookies over
localStorage, which is exposed to XSS. - Honor `expires_in`. Refresh proactively before expiry, not after a failed request.
- Treat refresh tokens like passwords. They are long-lived; leaking one is a serious breach. Rotate and revoke them.
- Check `scope`. The server may grant less than you asked for — handle the reduced permission gracefully.