Skip to main content
OAuth 2.0 & OpenID Connectlesson 3 of 4 · 2 min read

The Tokens Involved

Three credentials, three jobs

The exchange hands you up to three credentials, and confusing their jobs is a standard interview trap.

The access token is what your client presents to the API on every call. It is deliberately short-lived, often an hour. It might be a signed token the API verifies locally, or an opaque string it checks against the provider. A well-behaved client treats it as an opaque blob either way.

Use a refresh token so short access tokens do not mean asking your user to consent every hour. It lives a long time and goes only to the token endpoint, never to an API. Store it carefully: your own database for a web app, the secure store on a phone.

Rotate it, because it is such a valuable thing to steal. Each use issues a replacement and kills the old one. If a used refresh token ever turns up again, two parties clearly hold copies, so the provider revokes the entire grant.

Request narrow scopes, and not merely out of etiquette. That consent screen is part of your signup funnel, and an app demanding twelve permissions to show a calendar loses people right there. Derive your scopes from the API calls you actually make.

Machine to machine

Learn one more grant by name: client credentials, for machine-to-machine calls with no user anywhere in the picture.

The service authenticates with its own identifier and secret and gets a token representing itself. No consent screen, and usually no refresh token, since it can simply authenticate again whenever it likes.

Recognise it in the wild. Most internal service-to-service auth, and most things marketed as a modern replacement for API keys, are this flow wearing different clothes.

the shape of it
Auth serverAccess token~1 hour, for APIsID tokenwho logged inRefresh tokenlong-lived, storedResource APIYour app1a. for APIs1b. for you1c. to renew2a. Bearer header2b. verify + read3. swap for new
step 1 of 3
Access tokens go to APIs, the ID token stays in your app, and the refresh token only ever returns to the auth server.

Worked example

Ana's scheduling tool syncs with Google Calendar. At connect time her backend runs the code flow with the calendar.readonly scope and stores the refresh token encrypted in her own database; the access token that arrives alongside lasts 3,600 seconds, so her sync worker requests a fresh one before each hourly run. One Tuesday a user revokes the app from their Google security settings. The next refresh call returns HTTP 400 with invalid_grant, and because Ana coded for it, the worker marks the connection broken and emails a reconnect link instead of retrying forever. Months later, a migration mishap briefly exposes the tokens table. The postmortem stays calm: access tokens live an hour, and refresh tokens for all 4,100 connected accounts were revoked and reissued the same afternoon, so the exposure closes without a single unauthorized calendar read.