コンテンツにスキップ

Token Management

このコンテンツはまだ日本語訳がありません。

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)
  • Native SSO token exchange using device_secret
  • Token Introspection (RFC 7662)
  • Token Revocation (RFC 7009)

getAccessToken() returns a valid access token, automatically refreshing it if expired:

const accessToken = await client.token.getAccessToken();
// Use the token
const response = await fetch('https://api.example.com/data', {
headers: { Authorization: `Bearer ${accessToken}` },
});

The SDK handles the refresh flow automatically:

  1. Checks if the stored access token is still valid
  2. If expired (or within the refreshSkewSeconds window), uses the refresh token to obtain a new access token
  3. Saves the new tokens to storage
  4. Returns the new access token
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);
}
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);
}
const isAuthenticated = client.token.isAuthenticated();
PropertyTypeDescription
accessTokenstringOAuth 2.0 access token
refreshTokenstring | undefinedRefresh token
idTokenstring | undefinedOIDC ID token
tokenType'Bearer' | 'DPoP'Token type
expiresAtnumberExpiration time (epoch seconds)
scopestring | undefinedGranted scopes
refreshTokenExpiresAtnumber | undefinedRefresh token expiration time (epoch seconds), when returned
refreshTokenExpiresInnumber | undefinedRefresh token lifetime in seconds, when returned
refreshTokenExpiresAtIsostring | undefinedRefresh token expiration timestamp string, when returned

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);
ParameterTypeDefaultDescription
subjectTokenstringRequiredThe token to exchange
subjectTokenTypestring'access_token''access_token', 'refresh_token', 'id_token', or 'device_secret'
resourcestring | string[]Resource indicator for the new token
audiencestringTarget audience for the new token
scopestringRequested scopes for the new token
requestedTokenTypestringDesired token type
actorTokenstringActor token (for delegation)
actorTokenTypestring'access_token', 'id_token', or 'device_secret'
channel'browser' | 'native' | 'server'Product-specific exchange channel
PropertyTypeDescription
tokensTokenSetThe exchanged tokens
issuedTokenTypestringThe type of token issued
nativeSSOobject | undefinedNative SSO installation metadata when returned by the server

Use exchangeNativeSSO() when exchanging an ID Token and Native SSO device_secret. The request requires a DPoP proof for the token endpoint.

const dpop = client.dpop;
if (!dpop) {
throw new Error('Native SSO token exchange requires DPoP support');
}
await dpop.initialize();
const dpopProof = await dpop.generateProof('POST', 'https://auth.example.com/token');
const result = await client.token.exchangeNativeSSO({
idToken,
deviceSecret,
dpopProof,
scope: 'openid profile',
});
console.log(result.tokens.accessToken);
console.log(result.nativeSSO?.installationId);

Avoid logging the raw device_secret. Introspection and revocation also accept tokenTypeHint: 'device_secret'.

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');
}
ParameterTypeDefaultDescription
tokenstringRequiredThe token to introspect
tokenTypeHintstring'access_token', 'refresh_token', or 'device_secret'
PropertyTypeDescription
activebooleanWhether the token is active
scopestring | undefinedToken scopes
client_idstring | undefinedClient that requested the token
usernamestring | undefinedResource owner username
token_typestring | undefinedToken type
expnumber | undefinedExpiration timestamp
iatnumber | undefinedIssued at timestamp
substring | undefinedSubject identifier
audstring | undefinedAudience
issstring | undefinedIssuer
jtistring | undefinedToken identifier
installation_idstring | undefinedNative SSO installation id for device_secret introspection
app_display_namestring | undefinedNative SSO application display name
platformstring | undefinedNative SSO device platform

Revoke a token when it is no longer needed:

await client.token.revoke({
token: refreshToken,
tokenTypeHint: 'refresh_token',
});
ParameterTypeDefaultDescription
tokenstringRequiredThe token to revoke
tokenTypeHintstring'access_token', 'refresh_token', or 'device_secret'

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();

The SDK emits events during token operations. See Events for details.

EventDescription
token:refreshingToken refresh started
token:refreshedToken refresh succeeded
token:refresh:failedToken refresh failed
token:expiringToken is about to expire
token:expiredToken has expired
token:exchangedToken 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();
}
});