File

projects/lib/src/token-validation/jwks-validation-handler.ts

Description

Validates the signature of an id_token against one of the keys of an JSON Web Key Set (jwks).

This jwks can be provided by the discovery document.

Extends

AbstractValidationHandler

Index

Properties
Methods

Properties

allowedAlgorithms
allowedAlgorithms: string[]
Type : string[]
Default value : [ 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', 'RS512', 'ES256', 'ES384', 'PS256', 'PS384', 'PS512' ]

Allowed algorithms

gracePeriodInSec
gracePeriodInSec: number
Type : number
Default value : 600

Time period in seconds the timestamp in the signature can differ from the current time.

Methods

calcHash
calcHash(valueToHash: string, algorithm: string)
Parameters :
Name Type Optional
valueToHash string no
algorithm string no
Returns : string
toByteArrayAsString
toByteArrayAsString(hexString: string)
Parameters :
Name Type Optional
hexString string no
Returns : string
validateSignature
validateSignature(params: ValidationParams, retry: )
Parameters :
Name Type Optional Default value
params ValidationParams no
retry no false
Returns : Promise<any>
Protected calcHash
calcHash(valueToHash: string, algorithm: string)
Defined in AbstractValidationHandler:88

Calculates the hash for the passed value by using the passed hash algorithm.

Parameters :
Name Type Optional
valueToHash string no
algorithm string no
Returns : string
Protected inferHashAlgorithm
inferHashAlgorithm(jwtHeader: object)
Defined in AbstractValidationHandler:71

Infers the name of the hash algorithm to use from the alg field of an id_token.

Parameters :
Name Type Optional Description
jwtHeader object no

the id_token's parsed header

Returns : string
validateAtHash
validateAtHash(params: ValidationParams)
Defined in AbstractValidationHandler:42

Validates the at_hash in an id_token against the received access_token.

Parameters :
Name Type Optional
params ValidationParams no
Returns : boolean
validateSignature
validateSignature(validationParams: ValidationParams)
Defined in AbstractValidationHandler:37

Validates the signature of an id_token.

Parameters :
Name Type Optional
validationParams ValidationParams no
Returns : Promise<any>
import {
  AbstractValidationHandler,
  ValidationParams
} from './validation-handler';

declare var require: any;
let rs = require('jsrsasign');

/**
 * Validates the signature of an id_token against one
 * of the keys of an JSON Web Key Set (jwks).
 *
 * This jwks can be provided by the discovery document.
 */
export class JwksValidationHandler extends AbstractValidationHandler {
  /**
   * Allowed algorithms
   */
  allowedAlgorithms: string[] = [
    'HS256',
    'HS384',
    'HS512',
    'RS256',
    'RS384',
    'RS512',
    'ES256',
    'ES384',
    'PS256',
    'PS384',
    'PS512'
  ];

  /**
   * Time period in seconds the timestamp in the signature can
   * differ from the current time.
   */
  gracePeriodInSec = 600;

  validateSignature(params: ValidationParams, retry = false): Promise<any> {
    if (!params.idToken) throw new Error('Parameter idToken expected!');
    if (!params.idTokenHeader)
      throw new Error('Parameter idTokenHandler expected.');
    if (!params.jwks) throw new Error('Parameter jwks expected!');

    if (
      !params.jwks['keys'] ||
      !Array.isArray(params.jwks['keys']) ||
      params.jwks['keys'].length === 0
    ) {
      throw new Error('Array keys in jwks missing!');
    }

    // console.debug('validateSignature: retry', retry);

    let kid: string = params.idTokenHeader['kid'];
    let keys: object[] = params.jwks['keys'];
    let key: object;

    let 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(
        k => k['kty'] === kty && k['use'] === 'sig'
      );

      /*
            if (matchingKeys.length == 0) {
                let error = 'No matching key found.';
                console.error(error);
                return Promise.reject(error);
            }*/
      if (matchingKeys.length > 1) {
        let error =
          'More than one matching key found. Please specify a kid in the id_token header.';
        console.error(error);
        return Promise.reject(error);
      } else if (matchingKeys.length === 1) {
        key = matchingKeys[0];
      }
    }

    if (!key && !retry && params.loadKeys) {
      return params
        .loadKeys()
        .then(loadedKeys => (params.jwks = loadedKeys))
        .then(_ => this.validateSignature(params, true));
    }

    if (!key && retry && !kid) {
      let error = 'No matching key found.';
      console.error(error);
      return Promise.reject(error);
    }

    if (!key && retry && kid) {
      let error =
        'expected key not found in property jwks. ' +
        'This property is most likely loaded with the ' +
        'discovery document. ' +
        'Expected key id (kid): ' +
        kid;

      console.error(error);
      return Promise.reject(error);
    }

    let keyObj = rs.KEYUTIL.getKey(key);
    let validationOptions = {
      alg: this.allowedAlgorithms,
      gracePeriod: this.gracePeriodInSec
    };
    let isValid = rs.KJUR.jws.JWS.verifyJWT(
      params.idToken,
      keyObj,
      validationOptions
    );

    if (isValid) {
      return Promise.resolve();
    } else {
      return Promise.reject('Signature not valid');
    }
  }

  private alg2kty(alg: string) {
    switch (alg.charAt(0)) {
      case 'R':
        return 'RSA';
      case 'E':
        return 'EC';
      default:
        throw new Error('Cannot infer kty from alg: ' + alg);
    }
  }

  calcHash(valueToHash: string, algorithm: string): string {
    let hashAlg = new rs.KJUR.crypto.MessageDigest({ alg: algorithm });
    let result = hashAlg.digestString(valueToHash);
    let byteArrayAsString = this.toByteArrayAsString(result);
    return 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);
      result += String.fromCharCode(num);
    }
    return result;
  }
}

results matching ""

    No results matching ""