Skip to content

Tokens & TTL

Artifact Expirations (TTL)

Specifies the Time-To-Live (TTL) values that shall be applied to various artifacts within the authorization server. TTL values may be specified as either a numeric value (in seconds) or a synchronous function that returns a numeric value based on the current request context and authorization server policy.

recommendation: Token TTL values should be set to the minimum duration necessary for the intended use case to minimize security exposure.

recommendation: For refresh tokens requiring extended lifetimes, consider utilizing the rotateRefreshToken configuration option, which extends effective token lifetime through rotation rather than extended initial TTL values.

default value:

{
AccessToken: function AccessTokenTTL(ctx, token, client) {
return token.resourceServer?.accessTokenTTL || 60 * 60; // 1 hour in seconds
},
AuthorizationCode: 60 /* 1 minute in seconds */,
BackchannelAuthenticationRequest: function BackchannelAuthenticationRequestTTL(ctx, request, client) {
if (ctx?.oidc?.params.requested_expiry) {
return Math.min(10 * 60, +ctx.oidc.params.requested_expiry); // 10 minutes in seconds or requested_expiry, whichever is shorter
}
return 10 * 60; // 10 minutes in seconds
},
ClientCredentials: function ClientCredentialsTTL(ctx, token, client) {
return token.resourceServer?.accessTokenTTL || 10 * 60; // 10 minutes in seconds
},
DeviceCode: 600 /* 10 minutes in seconds */,
Grant: 1209600 /* 14 days in seconds */,
IdToken: 3600 /* 1 hour in seconds */,
Interaction: 3600 /* 1 hour in seconds */,
RefreshToken: function RefreshTokenTTL(ctx, token, client) {
if (
ctx?.oidc?.entities.RotatedRefreshToken
&& client.applicationType === 'web'
&& client.clientAuthMethod === 'none'
&& !token.isSenderConstrained()
) {
// Non-Sender Constrained SPA RefreshTokens do not have infinite expiration through rotation
return ctx.oidc.entities.RotatedRefreshToken.remainingTTL;
}
return 14 * 24 * 60 * 60; // 14 days in seconds
},
Session: 1209600 /* 14 days in seconds */
}

Example: (Click to expand) To resolve a ttl on runtime for each new token.

Configure ttl for a given token type with a function like so, this must return a value, not a Promise.

{
ttl: {
AccessToken(ctx, token, client) {
// return a Number (in seconds) for the given token (second argument), the associated client is
// passed as a third argument
// Tip: if the values are entirely client based memoize the results
return resolveTTLfor(token, client);
},
},
}

Session-Bound Token Expiration

Specifies a helper function that shall be invoked to determine whether authorization codes, device codes, or authorization-endpoint-returned opaque access tokens shall be bound to the end-user session. When session binding is enabled, this policy shall be applied to all opaque tokens issued from the authorization code, device code, or subsequent refresh token exchanges. When artifacts are session-bound, their originating session will be loaded by its unique identifier every time the artifacts are encountered. Session-bound artifacts shall be effectively revoked when the end-user logs out, providing automatic cleanup of token state upon session termination.

default value:

async function expiresWithSession(ctx, code) {
return !code.scopes.has('offline_access');
}

Refresh Token Issuance Policy

Specifies a helper function that shall be invoked to determine whether a refresh token shall be issued during token endpoint operations. This function enables policy-based control over refresh token issuance according to authorization server requirements, client capabilities, and granted scope values.

default value:

async function issueRefreshToken(ctx, client, code) {
return (
client.grantTypeAllowed('refresh_token')
&& code.scopes.has('offline_access')
);
}

Example: (Click to expand) To always issue a refresh token (cont.)

(cont.) if a client has the grant allowed and scope includes offline_access or the client is a public web client doing code flow. Configure issueRefreshToken like so

async issueRefreshToken(ctx, client, code) {
if (!client.grantTypeAllowed('refresh_token')) {
return false;
}
return code.scopes.has('offline_access') || (client.applicationType === 'web' && client.clientAuthMethod === 'none');
}

Refresh Token Rotation Policy

Specifies the refresh token rotation policy that shall be applied by the authorization server when refresh tokens are used. This configuration determines whether and under what conditions refresh tokens shall be rotated. Supported values include:

  • false - refresh tokens shall not be rotated and their initial expiration date is final
  • true - refresh tokens shall be rotated when used, with the current token marked as consumed and a new one issued with new TTL; when a consumed refresh token is encountered an error shall be returned and the whole token chain (grant) is revoked
  • function - a function returning true/false that shall be invoked to determine whether rotation should occur based on request context and authorization server policy

The default configuration value implements a sensible refresh token rotation policy that:

  • only allows refresh tokens to be rotated (have their TTL prolonged by issuing a new one) for one year
  • otherwise always rotates public client tokens that are not sender-constrained
  • otherwise only rotates tokens if they’re being used close to their expiration (>= 70% TTL passed)

default value:

function rotateRefreshToken(ctx) {
const { RefreshToken: refreshToken, Client: client } = ctx.oidc.entities;
// cap the maximum amount of time a refresh token can be
// rotated for up to 1 year, afterwards its TTL is final
if (refreshToken.totalLifetime() >= 365.25 * 24 * 60 * 60) {
return false;
}
// rotate non sender-constrained public client refresh tokens
if (
client.clientAuthMethod === 'none'
&& !refreshToken.isSenderConstrained()
) {
return true;
}
// rotate if the token is nearing expiration (it's beyond 70% of its lifetime)
return refreshToken.ttlPercentagePassed() >= 70;
}

Additional Access Token Claims

Specifies a helper function that shall be invoked to add additional claims to Access Tokens during the token issuance process. For opaque Access Tokens, the returned claims shall be stored in the authorization server storage under the extra property and shall be returned by the introspection endpoint as top-level claims. For JWT-formatted Access Tokens, the returned claims shall be included as top-level claims within the JWT payload. Claims returned by this function will not overwrite pre-existing top-level claims in the token.

default value:

async function extraTokenClaims(ctx, token) {
return undefined;
}

Example: (Click to expand) To add an arbitrary claim to an Access Token.

{
async extraTokenClaims(ctx, token) {
return {
'urn:idp:example:foo': 'bar',
};
}
}

Specifies the entropy configuration for opaque token generation. The value shall be an integer (or a function returning an integer) that determines the cryptographic strength of generated opaque tokens. The resulting opaque token length shall be calculated as Math.ceil(i / Math.log2(n)) where i is the specified bit count and n is the number of symbols in the encoding alphabet (64 characters in the base64url character set used by this implementation).

default value:

256

Example: (Click to expand) To have e.g. Refresh Tokens values longer than Access Tokens.

function bitsOfOpaqueRandomness(ctx, token) {
if (token.kind === 'RefreshToken') {
return 384;
}
return 256;
}

Specifies customizer functions that shall be invoked immediately before issuing structured Access Tokens to enable modification of token headers and payload claims according to authorization server policy. These functions shall be called during the token formatting process to apply deployment-specific customizations to the token structure before signing.

default value:

{
jwt: undefined
}

Example: (Click to expand) To add additional headers and payload claims to a jwt format Access Token.

{
customizers: {
async jwt(ctx, token, jwt) {
jwt.header = { foo: 'bar' };
jwt.payload.foo = 'bar';
}
}
}