UNPKG

oidc-provider

Version:

OAuth 2.0 Authorization Server implementation for Node.js with OpenID Connect

74 lines (61 loc) 2.33 kB
import { InvalidClientAuth } from '../helpers/errors.js'; import instance from '../helpers/weak_cache.js'; import * as JWT from '../helpers/jwt.js'; export default function getJwtClientAuth(provider) { const { clockTolerance, assertJwtClientAuthClaimsAndHeader } = instance(provider).configuration; return async function jwtClientAuth( ctx, keystore, algorithms, ) { const acceptedAud = ctx.oidc.clientJwtAuthExpectedAudience(); const { header, payload } = JWT.decode(ctx.oidc.params.client_assertion); if (!algorithms.includes(header.alg)) { throw new InvalidClientAuth('alg mismatch'); } if (!payload.exp) { throw new InvalidClientAuth('expiration must be specified in the client_assertion JWT'); } if (!payload.jti) { throw new InvalidClientAuth('unique jti (JWT ID) must be provided in the client_assertion JWT'); } if (!payload.iss) { throw new InvalidClientAuth('iss (JWT issuer) must be provided in the client_assertion JWT'); } if (payload.iss !== ctx.oidc.client.clientId) { throw new InvalidClientAuth('iss (JWT issuer) must be the client_id'); } if (!payload.aud) { throw new InvalidClientAuth('aud (JWT audience) must be provided in the client_assertion JWT'); } if (Array.isArray(payload.aud)) { if (!payload.aud.some((aud) => acceptedAud.has(aud))) { throw new InvalidClientAuth('list of audience (aud) must include the endpoint url, issuer identifier or token endpoint url'); } } else if (!acceptedAud.has(payload.aud)) { throw new InvalidClientAuth('audience (aud) must equal the endpoint url, issuer identifier or token endpoint url'); } try { await JWT.verify(ctx.oidc.params.client_assertion, keystore, { clockTolerance, ignoreAzp: true, }); } catch (err) { throw new InvalidClientAuth(err.message); } await assertJwtClientAuthClaimsAndHeader( ctx, structuredClone(payload), structuredClone(header), ctx.oidc.client, ); const unique = await provider.ReplayDetection.unique( payload.iss, payload.jti, payload.exp + clockTolerance, ); if (!unique) { throw new InvalidClientAuth('client assertion tokens must only be used once'); } }; }