module license /** */ import * as jose from 'node-jose' ; import { AuthClaimsHandler , TokenValidationError } from './token_auth_handler' ; /**
 * The authentication claims are given in JWT as a string map.
 */ export interface EncodedJwtAuthClaims { [ key ] : string ; } /**
 * Decodes raw JWT claims into a custom data type.
 */ export type JwtAuthClaimsDecoder < AuthClaims > = ( encodedClaims : EncodedJwtAuthClaims ) => Promise < AuthClaims > ; param jwksUrl param expectedAudiences param decodeClaims throws export function getJwtAuthClaimsHandler < AuthClaims > ( jwksUrl , expectedAudiences , decodeClaims ) { /** The JWKS, or null if not already (lazily) fetched */ let jwks ; /**
     * Lazily initiated `verify` functions (from the `node-jose` package), one for each
     * key (indexation by the key ID).
     */ const verify = { } ; function getKey ( jwks , keyId ) { const key = jwks . keys . find ( ( key ) => key . kid === keyId ) ; if ( ! key ) { throw new TokenValidationError ( `cannot find key ${ keyId } in JWKS` ) ; } return key ; } async function getVerify ( keyId ) { if ( ! verify [ keyId ] ) { if ( ! jwks ) { jwks = await fetchJwks ( jwksUrl ) ; } const key = getKey ( jwks , keyId ) ; const result = await jose . JWK . asKey ( key ) ; verify [ keyId ] = jose . JWS . createVerify ( result ) ; } return verify [ keyId ] ; } return async ( token ) => { let jwt ; try { const sections = token . split ( '.' ) ; jwt = JSON . parse ( jose . util . base64url . decode ( sections [ 0 ] ) ) ; } catch ( err ) { throw new TokenValidationError ( `cannot parse token \'${ token }\'` ) ; } const verify = await getVerify ( jwt . kid ) ; let result ; try { result = await verify . verify ( token ) ; } catch ( err ) { throw new TokenValidationError ( `cannot verify token: ${ err . stack }` ) ; } const claims = JSON . parse ( result . payload ) ; checkClaims ( expectedAudiences , claims ) ; return decodeClaims ( claims ) ; } ; } function checkClaims ( expectedAudiences , claims ) { if ( ! claims . exp || getCurrentTs ( ) > claims . exp ) { throw new TokenValidationError ( 'token has expired or exp is not set' ) ; } if ( ! expectedAudiences . includes ( claims . aud ) ) { throw new TokenValidationError ( `aud (token audience) was not expected` ) ; } if ( ! claims . sub ) { throw new TokenValidationError ( 'sub (token subject) is not set' ) ; } } async function fetchJwks ( jwksUrl ) { const res = await fetch ( jwksUrl ) ; if ( ! res . ok ) { throw new Error ( 'cannot fetch JWT keys' ) ; } return res . json ( ) ; } /** Get the current timestamp, in seconds, to compare it to the JWT's expiry date. */ function getCurrentTs ( ) { return Math . floor ( new Date ( ) . getTime ( ) / 1000 ) ; }