diff --git a/projects/angular-oauth2-oidc-jwks/src/lib/jwks-validation-handler.ts b/projects/angular-oauth2-oidc-jwks/src/lib/jwks-validation-handler.ts index 227666c8..9f60a4c5 100644 --- a/projects/angular-oauth2-oidc-jwks/src/lib/jwks-validation-handler.ts +++ b/projects/angular-oauth2-oidc-jwks/src/lib/jwks-validation-handler.ts @@ -1,8 +1,10 @@ import * as rs from 'jsrsasign'; import { AbstractValidationHandler, + DateTimeProvider, ValidationParams, } from 'angular-oauth2-oidc'; +import { Injector } from '@angular/core'; /** * Validates the signature of an id_token against one @@ -11,6 +13,9 @@ import { * This jwks can be provided by the discovery document. */ export class JwksValidationHandler extends AbstractValidationHandler { + constructor(private injector?: Injector) { + super(); + } /** * Allowed algorithms */ @@ -50,17 +55,17 @@ export class JwksValidationHandler extends AbstractValidationHandler { // console.debug('validateSignature: retry', retry); - let kid: string = params.idTokenHeader['kid']; - let keys: object[] = params.jwks['keys']; + const kid: string = params.idTokenHeader['kid']; + const keys: object[] = params.jwks['keys']; let key: object; - let alg = params.idTokenHeader['alg']; + const alg = params.idTokenHeader['alg']; if (kid) { key = keys.find((k) => k['kid'] === kid /* && k['use'] === 'sig' */); } else { - let kty = this.alg2kty(alg); - let matchingKeys = keys.filter( + const kty = this.alg2kty(alg); + const matchingKeys = keys.filter( (k) => k['kty'] === kty && k['use'] === 'sig' ); @@ -71,7 +76,7 @@ export class JwksValidationHandler extends AbstractValidationHandler { return Promise.reject(error); }*/ if (matchingKeys.length > 1) { - let error = + const error = 'More than one matching key found. Please specify a kid in the id_token header.'; console.error(error); return Promise.reject(error); @@ -88,13 +93,13 @@ export class JwksValidationHandler extends AbstractValidationHandler { } if (!key && retry && !kid) { - let error = 'No matching key found.'; + const error = 'No matching key found.'; console.error(error); return Promise.reject(error); } if (!key && retry && kid) { - let error = + const error = 'expected key not found in property jwks. ' + 'This property is most likely loaded with the ' + 'discovery document. ' + @@ -105,16 +110,14 @@ export class JwksValidationHandler extends AbstractValidationHandler { return Promise.reject(error); } - let keyObj = rs.KEYUTIL.getKey(key); - let validationOptions = { + const keyObj = this.getKey(key); + const validationOptions = { alg: this.allowedAlgorithms, gracePeriod: this.gracePeriodInSec, + verifyAt: this.verifyAt, }; - let isValid = rs.KJUR.jws.JWS.verifyJWT( - params.idToken, - keyObj, - validationOptions - ); + + const isValid = this.verifyJWT(params.idToken, keyObj, validationOptions); if (isValid) { return Promise.resolve(); @@ -123,7 +126,15 @@ export class JwksValidationHandler extends AbstractValidationHandler { } } - private alg2kty(alg: string) { + getKey(key: object) { + return this.rs.KEYUTIL.getKey(key); + } + + verifyJWT(idToken: string, keyObj: object, validationOptions: object) { + return this.rs.KJUR.jws.JWS.verifyJWT(idToken, keyObj, validationOptions); + } + + alg2kty(alg: string) { switch (alg.charAt(0)) { case 'R': return 'RSA'; @@ -135,19 +146,33 @@ export class JwksValidationHandler extends AbstractValidationHandler { } calcHash(valueToHash: string, algorithm: string): Promise { - let hashAlg = new rs.KJUR.crypto.MessageDigest({ alg: algorithm }); - let result = hashAlg.digestString(valueToHash); - let byteArrayAsString = this.toByteArrayAsString(result); + const hashAlg = new rs.KJUR.crypto.MessageDigest({ alg: algorithm }); + const result = hashAlg.digestString(valueToHash); + const byteArrayAsString = this.toByteArrayAsString(result); return Promise.resolve(byteArrayAsString); } toByteArrayAsString(hexString: string) { let result = ''; for (let i = 0; i < hexString.length; i += 2) { - let hexDigit = hexString.charAt(i) + hexString.charAt(i + 1); - let num = parseInt(hexDigit, 16); + const hexDigit = hexString.charAt(i) + hexString.charAt(i + 1); + const num = parseInt(hexDigit, 16); result += String.fromCharCode(num); } return result; } + + get rs() { + return rs; + } + + private get dateTimeProvider() { + return this.injector?.get(DateTimeProvider); + } + + private get verifyAt() { + const now = this.dateTimeProvider?.new() || new Date(); + const verifyAt = now.getTime() / 1000; + return verifyAt; + } }