Skip to content

[Issue #1477] Use provided date for token validation #1475

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
*/
Expand Down Expand Up @@ -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'
);

Expand All @@ -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);
Expand All @@ -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. ' +
Expand All @@ -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();
Expand All @@ -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';
Expand All @@ -135,19 +146,33 @@ export class JwksValidationHandler extends AbstractValidationHandler {
}

calcHash(valueToHash: string, algorithm: string): Promise<string> {
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;
}
}