Refresh Token
The Refresh Token grant (RFC 6749 §6) lets a client obtain a new access token without prompting the user to sign in again. The client sends its refresh token to the token endpoint and receives a new access token (and optionally a new refresh token) in return.
Refresh tokens are long-lived credentials and must be treated as such: they replace the user's session. ThunderID supports rotation so refresh tokens themselves can be short-lived in practice.
How It Works
The refresh request goes directly to the token endpoint and returns a new token bundle.
POST /oauth2/token HTTP/1.1
Host: thunderid.example.com
Authorization: Basic <client_id:client_secret>
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
&refresh_token=<refresh_token>
&scope=openid%20profile # optional, narrow only
&resource=https://api.example.com # optional, must match the existing audience
Response (rotation enabled):
{
"access_token": "eyJhbGciOi...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "eyJhbGciOi...",
"scope": "openid profile"
}
How ThunderID Implements It
| Aspect | Behavior |
|---|---|
| Endpoint | POST /oauth2/token with grant_type=refresh_token |
| Client authentication | Same methods as the original grant, see Client Authentication Methods |
| Rotation | Controlled globally by oauth.refresh_token.renew_on_grant in deployment.yaml. When enabled, every refresh issues a new refresh_token; when disabled, the existing one is reused |
| Old token after rotation | When oauth.refresh_token.revoke_previous_on_renew is enabled, the consumed refresh token is revoked after renewal. A revocation failure stops the rotation |
| Audience preservation | The new access token keeps the refresh token's single audience when resource is omitted |
| Resource validation | When supplied, resource must match the refresh token's audience. A mismatch returns invalid_target |
| Scope narrowing | The scope parameter may request a subset of the originally granted scopes. Asking for a wider scope returns invalid_scope |
| DPoP binding | A DPoP-bound refresh token continues to require a DPoP proof on refresh; the new access token inherits the cnf.jkt binding |
What Changes Between the Original and Refreshed Token
| Claim | Behavior on refresh |
|---|---|
sub | Unchanged |
iss | Unchanged |
aud | Preserved. A supplied resource must match this value |
scope | Preserved by default; narrowed when scope is supplied |
exp / iat | New values |
cnf.jkt (DPoP) | Preserved |
auth_time | Not carried into refreshed tokens |
Try It in ThunderID
- Console
- Dynamic Client Registration
- Open Applications or Agents in the ThunderID Console and select your client.
- Open the Advanced Settings tab.
- Under Grant Types, add
refresh_tokenalongside a token-issuing grant (authorization_codeorurn:openid:params:grant-type:ciba). - Save.
POST /oauth2/dcr/register
Content-Type: application/json
{
"client_name": "My App",
"redirect_uris": ["https://app.example.com/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"]
}
Refresh token rotation is a deployment-wide setting, not per client. Enable it by setting oauth.refresh_token.renew_on_grant: true in deployment.yaml. By default, oauth.refresh_token.revoke_previous_on_renew is true, so a successful rotation revokes the consumed refresh token.
Related Guides
- Authorization Code, the most common source of refresh tokens
- Resource Indicators, preserve and validate the resource binding during refresh
- DPoP, refresh tokens issued under DPoP remain sender-constrained
- Token Formats, token lifetimes, signing and encryption settings