diff --git a/docs/README.md b/docs/README.md index 28e251177..751a4f87f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2288,6 +2288,7 @@ _**default value**_: { AccessToken: 'opaque', ClientCredentials: 'opaque', + bitsOfOpaqueRandomness: 256, customizers: { 'jwt-ietf': undefined, jwt: undefined @@ -2320,6 +2321,29 @@ Configure `formats`: ``` +### 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**_: +```js +256 +``` +
(Click to expand) To have e.g. Refresh Tokens values longer than Access Tokens. +
+ +```js +function bitsOfOpaqueRandomness(ctx, token) { + if (token.kind === 'RefreshToken') { + return 384; + } + return 256; +} +``` +
+ ### formats.customizers Functions used before signing a structured Access Token of a given type, such as a JWT one. Customizing here only changes the structured Access Token, not your storage, introspection or anything else. For such extras use [`extraTokenClaims`](#extratokenclaims) instead. diff --git a/lib/helpers/defaults.js b/lib/helpers/defaults.js index 991be2b1b..9313a6720 100644 --- a/lib/helpers/defaults.js +++ b/lib/helpers/defaults.js @@ -1698,6 +1698,26 @@ function getDefaults() { * This helper should resolve with a JWS Algorithm string. */ tokenSigningAlg, + + /* + * formats.bitsOfOpaqueRandomness + * + * description: 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. + * + * example: To have e.g. Refresh Tokens values longer than Access Tokens. + * ```js + * function bitsOfOpaqueRandomness(ctx, token) { + * if (token.kind === 'RefreshToken') { + * return 384; + * } + * + * return 256; + * } + * ``` + */ + bitsOfOpaqueRandomness: 256, AccessToken: 'opaque', ClientCredentials: 'opaque', diff --git a/lib/models/formats/opaque.js b/lib/models/formats/opaque.js index 0658e3af5..1ea6e59df 100644 --- a/lib/models/formats/opaque.js +++ b/lib/models/formats/opaque.js @@ -8,12 +8,21 @@ const nanoid = require('../../helpers/nanoid'); const ctxRef = require('../ctx_ref'); const withExtra = new Set(['AccessToken', 'ClientCredentials']); +const bitsPerSymbol = Math.log2(64); +const tokenLength = (i) => Math.ceil(i / bitsPerSymbol); module.exports = (provider) => ({ - // Default nanoid has a (26+26+10+2 = 64) symbol alphabet (6 bits). So with 6 bits per symbol, and - // 43 symbols => (6*27 = 258) total bits. generateTokenId() { - return nanoid(43); + let length; + if (this.kind !== 'PushedAuthorizationRequest') { + const bitsOfOpaqueRandomness = instance(provider).configuration('formats.bitsOfOpaqueRandomness'); + if (typeof bitsOfOpaqueRandomness === 'function') { + length = tokenLength(bitsOfOpaqueRandomness(ctxRef.get(this), this)); + } else { + length = tokenLength(bitsOfOpaqueRandomness); + } + } + return nanoid(length); }, async getValueAndPayload() { const now = epochTime(); diff --git a/types/index.d.ts b/types/index.d.ts index bed9d9dea..432a84a8c 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -972,6 +972,7 @@ export interface Configuration { AccessToken?: AccessTokenFormatFunction | TokenFormat; ClientCredentials?: ClientCredentialsFormatFunction | TokenFormat; tokenSigningAlg?: (ctx: KoaContextWithOIDC, token: AccessToken | ClientCredentials) => CanBePromise; + bitsOfOpaqueRandomness?: number | ((ctx: KoaContextWithOIDC, token: BaseToken) => number); customizers?: { jwt?: (ctx: KoaContextWithOIDC, token: AccessToken | ClientCredentials, parts: JWTStructured) => CanBePromise; 'jwt-ietf'?: (ctx: KoaContextWithOIDC, token: AccessToken | ClientCredentials, parts: JWTStructured) => CanBePromise;