Token Management
Overview
The Token Management API (client.token) provides methods for managing OAuth 2.0 tokens throughout their lifecycle:
- Retrieving access tokens with automatic refresh
- Token Exchange (RFC 8693)
- Token Introspection (RFC 7662)
- Token Revocation (RFC 7009)
Getting Access Tokens
Automatic Refresh
getAccessToken() returns a valid access token, automatically refreshing it if expired:
const accessToken = await client.token.getAccessToken();
// Use the tokenconst response = await fetch('https://api.example.com/data', { headers: { Authorization: `Bearer ${accessToken}` },});The SDK handles the refresh flow automatically:
- Checks if the stored access token is still valid
- If expired (or within the
refreshSkewSecondswindow), uses the refresh token to obtain a new access token - Saves the new tokens to storage
- Returns the new access token
Getting the Full Token Set
const tokens = await client.token.getTokens();
if (tokens) { console.log(tokens.accessToken); console.log(tokens.refreshToken); console.log(tokens.idToken); console.log(tokens.expiresAt); // epoch seconds console.log(tokens.scope);}Getting the ID Token
const idToken = await client.token.getIdToken();
if (idToken) { // Decode claims (for display purposes only — always verify on server) const claims = decodeIdToken(idToken); console.log(claims.sub, claims.name, claims.email);}Checking Authentication State
const isAuthenticated = client.token.isAuthenticated();TokenSet
| Property | Type | Description |
|---|---|---|
accessToken | string | OAuth 2.0 access token |
refreshToken | string | undefined | Refresh token |
idToken | string | undefined | OIDC ID token |
tokenType | 'Bearer' | Token type |
expiresAt | number | Expiration time (epoch seconds) |
scope | string | undefined | Granted scopes |
Token Exchange (RFC 8693)
Token Exchange allows you to exchange one token for another — for example, exchanging an access token for one with a different audience (impersonation, delegation, cross-service calls).
const result = await client.token.exchange({ subjectToken: currentAccessToken, subjectTokenType: 'access_token', audience: 'https://api.internal.example.com', scope: 'read:data',});
console.log(result.tokens.accessToken);console.log(result.issuedTokenType);TokenExchangeRequest
| Parameter | Type | Default | Description |
|---|---|---|---|
subjectToken | string | Required | The token to exchange |
subjectTokenType | string | 'access_token' | 'access_token', 'refresh_token', or 'id_token' |
audience | string | — | Target audience for the new token |
scope | string | — | Requested scopes for the new token |
requestedTokenType | string | — | Desired token type |
actorToken | string | — | Actor token (for delegation) |
actorTokenType | string | — | Actor token type |
TokenExchangeResult
| Property | Type | Description |
|---|---|---|
tokens | TokenSet | The exchanged tokens |
issuedTokenType | string | The type of token issued |
Token Introspection (RFC 7662)
Introspection checks whether a token is active and retrieves its metadata:
const info = await client.token.introspect({ token: accessToken, tokenTypeHint: 'access_token',});
if (info.active) { console.log('Token is valid'); console.log('Subject:', info.sub); console.log('Scopes:', info.scope); console.log('Expires:', info.exp);} else { console.log('Token is inactive or expired');}IntrospectTokenOptions
| Parameter | Type | Default | Description |
|---|---|---|---|
token | string | Required | The token to introspect |
tokenTypeHint | string | — | 'access_token', 'refresh_token', or 'id_token' |
IntrospectionResponse
| Property | Type | Description |
|---|---|---|
active | boolean | Whether the token is active |
scope | string | undefined | Token scopes |
client_id | string | undefined | Client that requested the token |
username | string | undefined | Resource owner username |
token_type | string | undefined | Token type |
exp | number | undefined | Expiration timestamp |
iat | number | undefined | Issued at timestamp |
sub | string | undefined | Subject identifier |
aud | string | undefined | Audience |
iss | string | undefined | Issuer |
jti | string | undefined | Token identifier |
Token Revocation (RFC 7009)
Revoke a token when it is no longer needed:
await client.token.revoke({ token: refreshToken, tokenTypeHint: 'refresh_token',});RevokeTokenOptions
| Parameter | Type | Default | Description |
|---|---|---|---|
token | string | Required | The token to revoke |
tokenTypeHint | string | — | 'access_token' or 'refresh_token' |
Multi-Tab Token Refresh
When multiple browser tabs are open, the SDK prevents concurrent refresh requests using an operation ID mechanism:
- The leader tab performs the refresh
- Other tabs wait for the leader to complete and then read the updated tokens from storage
- This prevents token race conditions and unnecessary refresh requests
// The SDK handles this automatically.// Each tab calls getAccessToken() and the SDK coordinates internally.const accessToken = await client.token.getAccessToken();Token Events
The SDK emits events during token operations. See Events for details.
| Event | Description |
|---|---|
token:refreshing | Token refresh started |
token:refreshed | Token refresh succeeded |
token:refresh_failed | Token refresh failed |
token:expiring | Token is about to expire |
token:expired | Token has expired |
token:exchanged | Token exchange completed |
client.on('token:refreshed', (event) => { console.log('Token refreshed, expires at:', event.expiresAt);});
client.on('token:refresh_failed', (event) => { console.log('Refresh failed:', event.error.code); if (!event.willRetry) { // Final failure — redirect to login redirectToLogin(); }});Next Steps
- Session & Logout — Session management and logout
- DPoP — Bind tokens to cryptographic keys
- Events — Full event reference