From caa9ab3233942faec8d1fcadcb26cd75bb3854cd Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Thu, 20 Jun 2024 10:54:50 +0200 Subject: [PATCH] fix: allow ID Token auth_time to be present even if client.require_auth_time is false --- src/index.ts | 14 +++++++--- test/authorization_code.test.ts | 47 +++++++++++++++------------------ 2 files changed, 32 insertions(+), 29 deletions(-) diff --git a/src/index.ts b/src/index.ts index ea11901c..ae1e4d0e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2715,8 +2715,11 @@ async function processGenericAccessTokenResponse( throw new OPE('unexpected ID Token "azp" (authorized party) claim value') } - if (client.require_auth_time && typeof claims.auth_time !== 'number') { - throw new OPE('unexpected ID Token "auth_time" (authentication time) claim value') + if ( + claims.auth_time !== undefined && + (!Number.isFinite(claims.auth_time) || Math.sign(claims.auth_time) !== 1) + ) { + throw new OPE('ID Token "auth_time" (authentication time) must be a positive number') } idTokenClaims.set(json, claims) @@ -3937,8 +3940,11 @@ export async function validateDetachedSignatureResponse( throw new OPE('invalid ID Token "s_hash" (state hash) claim value') } - if (client.require_auth_time !== undefined && typeof claims.auth_time !== 'number') { - throw new OPE('unexpected ID Token "auth_time" (authentication time) claim value') + if ( + claims.auth_time !== undefined && + (!Number.isFinite(claims.auth_time) || Math.sign(claims.auth_time) !== 1) + ) { + throw new OPE('ID Token "auth_time" (authentication time) must be a positive number') } maxAge ??= client.default_max_age ?? skipAuthTimeCheck diff --git a/test/authorization_code.test.ts b/test/authorization_code.test.ts index 7fab4eb5..13219073 100644 --- a/test/authorization_code.test.ts +++ b/test/authorization_code.test.ts @@ -828,32 +828,29 @@ test('processAuthorizationCodeOpenIDResponse() nonce checks', async (t) => { test('processAuthorizationCodeOpenIDResponse() auth_time checks', async (t) => { const tIssuer: lib.AuthorizationServer = { ...issuer, jwks_uri: endpoint('jwks') } - await t.throwsAsync( - lib.processAuthorizationCodeOpenIDResponse( - tIssuer, - { - ...client, - require_auth_time: true, - }, - getResponse( - JSON.stringify({ - access_token: 'token', - token_type: 'Bearer', - id_token: await new jose.SignJWT({ - auth_time: '0', - }) - .setProtectedHeader({ alg: 'RS256' }) - .setIssuer(issuer.issuer) - .setSubject('urn:example:subject') - .setAudience(client.client_id) - .setExpirationTime('5m') - .setIssuedAt() - .sign(t.context.RS256.privateKey), - }), + for (const auth_time of [0, -1, null, '1', [], {}, true]) { + await t.throwsAsync( + lib.processAuthorizationCodeOpenIDResponse( + tIssuer, + client, + getResponse( + JSON.stringify({ + access_token: 'token', + token_type: 'Bearer', + id_token: await new jose.SignJWT({ auth_time }) + .setProtectedHeader({ alg: 'RS256' }) + .setIssuer(issuer.issuer) + .setSubject('urn:example:subject') + .setAudience(client.client_id) + .setExpirationTime('5m') + .setIssuedAt() + .sign(t.context.RS256.privateKey), + }), + ), ), - ), - { message: 'unexpected ID Token "auth_time" (authentication time) claim value' }, - ) + { message: 'ID Token "auth_time" (authentication time) must be a positive number' }, + ) + } }) test('processAuthorizationCodeOpenIDResponse() azp checks', async (t) => {