Tokens and lifetimes
What lives how long, how to refresh, and why presenting an old refresh token twice is dangerous.
What you get
| Token | Lives | Purpose |
|---|---|---|
access_token | 1 hour | requests to /me |
id_token | 1 hour | proof of identity at sign-in |
refresh_token | as long as the auth.my session | refresh without signing in again |
refresh_token with offline_access | 90 days | access that outlives the user's sign-out |
The difference between the two refresh modes matters. An ordinary site wants
the first: sign-in does not fall over after an hour, and when the person signs
out of auth.my their access ends with the session, exactly as they expect.
Long-lived access to their data while they are away is offline_access, and
they approve it as a separate line on the consent screen.
The profile is inside the id_token
The specification allows omitting scope-requested data from the id_token
and sending clients to /me for it. We include it — nearly every library
reads name and email from there.
{
"sub": "65df5d53-a1fa-4dd1-ab0a-7f5ecc0ef3a0",
"email": "[email protected]",
"email_verified": true,
"iss": "https://auth.my",
"aud": "your-client-id"
}
A call to /me is only needed if the data may have changed since sign-in.
Signing
id_token is signed with RS256 — the algorithm every library understands
without configuration. Discovery also advertises ES256: choose it explicitly
if you want it, the key is there.
Signing keys rotate without notice. That is exactly what /jwks is for, and
libraries fetch from it themselves.
Do not hard-code or pin the key. Key rotation is routine, and an application with a baked-in key breaks the moment it happens.
Refresh
curl -u "CLIENT_ID:CLIENT_SECRET" https://auth.my/token \
-d grant_type=refresh_token \
-d refresh_token=CURRENT_TOKEN
The response carries a new refresh token. Store it in place of the old one.
Rotation is single-use: each exchange retires the previous token. That is how theft detection works — if a stolen token is presented after the legitimate owner has already used it, we see it and revoke the whole chain.
The race window
Strict single use breaks ordinary life: two tabs refresh at once, a mobile client retries after a dropped connection, two application processes refresh in parallel.
So a repeat presentation within 30 seconds counts as a race, not theft, and is served. Later than that it is treated as theft: every token on the grant is revoked and the user must sign in again.
The window is measured from first use and repeats do not extend it.
What to do about invalid_grant
It is not an outage, it is a message saying "this token is finished". Causes: the chain was revoked, the user blocked your site, the auth.my session ended, an organization administrator removed their access.
Handle them all the same way: send the user to sign in again. In the normal case they return instantly without noticing.
Revoking your own token
curl -u "CLIENT_ID:CLIENT_SECRET" https://auth.my/token/revocation \
-d token=TOKEN
This kills one token without touching the user's auth.my session. Full sign-out is in Sign-out and revocation.