Tokens & TTL
This content is for v8.x. Switch to the latest version for up-to-date documentation.
description: Expirations for various token and session types. The value can be a number (in seconds) or a synchronous function that dynamically returns value based on the context.
recommendation: Do not set token TTLs longer then they absolutely have to be, the shorter the TTL, the better.
recommendation: Rather than setting crazy high Refresh Token TTL look into rotateRefreshToken configuration option which is set up in way that when refresh tokens are regularly used they will have their TTL refreshed (via rotation).
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 && 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 && 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 */}(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 (first argument), the associated client is // passed as a second argument // Tip: if the values are entirely client based memoize the results return resolveTTLfor(token, client); }, },}expiresWithSession
Section titled “expiresWithSession”Function used to decide whether the given authorization code, device code, or authorization-endpoint returned opaque access token be bound to the user session. This will be applied to all opaque tokens issued from the authorization code, device code, or subsequent refresh token use in the future. When artifacts are session-bound their originating session will be loaded by its uid every time they are encountered. Session bound artefacts will effectively get revoked if the end-user logs out.
default value:
async function expiresWithSession(ctx, code) { return !code.scopes.has('offline_access');}issueRefreshToken
Section titled “issueRefreshToken”Function used to decide whether a refresh token will be issued or not
default value:
async function issueRefreshToken(ctx, client, code) { return client.grantTypeAllowed('refresh_token') && code.scopes.has('offline_access');}(Click to expand) To always issue a refresh tokens …
… 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');}rotateRefreshToken
Section titled “rotateRefreshToken”Configures if and how the authorization server rotates refresh tokens after they are used. Supported values are
falserefresh tokens are not rotated and their initial expiration date is finaltruerefresh tokens are rotated when used, current token is marked as consumed and new one is issued with new TTL, when a consumed refresh token is encountered an error is returned instead and the whole token chain (grant) is revokedfunctionreturning true/false, true when rotation should occur, false when it shouldn’t
The default configuration value puts forth a sensible refresh token rotation policy- only allows refresh tokens to be rotated (have their TTL prolonged by issuing a new one) for one year
- otherwise always rotate public client tokens that are not sender-constrained
- otherwise only rotate 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;}extraTokenClaims
Section titled “extraTokenClaims”Function used to add additional claims to an Access Token when it is being issued. For opaque Access Tokens these claims will be stored in your storage under the extra property and returned by introspection as top level claims. For jwt Access Tokens these will be top level claims. Returned claims will not overwrite pre-existing top level claims.
default value:
async function extraTokenClaims(ctx, token) { return undefined;}(Click to expand) To add an arbitrary claim to an Access Token
{ async extraTokenClaims(ctx, token) { return { 'urn:idp:example:foo': 'bar', }; }}formats.bitsOfOpaqueRandomness
Section titled “formats.bitsOfOpaqueRandomness”The value should be an integer (or a function returning an integer) and the resulting opaque token length is equal to Math.ceil(i / Math.log2(n)) where n is the number of symbols in the used alphabet, 64 in our case.
default value:
256(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;}formats.customizers
Section titled “formats.customizers”Customizer functions used before issuing a structured Access Token.
default value:
{ jwt: undefined}(Click to expand) To push 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'; } }}