oidc-client
Version:
OpenID Connect (OIDC) & OAuth2 client library
154 lines (132 loc) • 6.55 kB
JavaScript
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
import { Log } from './Log.js';
export default function getJoseUtil({ jws, KeyUtil, X509, crypto, hextob64u, b64tohex, AllowedSigningAlgs }) {
return class JoseUtil {
static parseJwt(jwt) {
Log.debug("JoseUtil.parseJwt");
try {
var token = jws.JWS.parse(jwt);
return {
header: token.headerObj,
payload: token.payloadObj
}
} catch (e) {
Log.error(e);
}
}
static validateJwt(jwt, key, issuer, audience, clockSkew, now, timeInsensitive) {
Log.debug("JoseUtil.validateJwt");
try {
if (key.kty === "RSA") {
if (key.e && key.n) {
key = KeyUtil.getKey(key);
} else if (key.x5c && key.x5c.length) {
var hex = b64tohex(key.x5c[0]);
key = X509.getPublicKeyFromCertHex(hex);
} else {
Log.error("JoseUtil.validateJwt: RSA key missing key material", key);
return Promise.reject(new Error("RSA key missing key material"));
}
} else if (key.kty === "EC") {
if (key.crv && key.x && key.y) {
key = KeyUtil.getKey(key);
} else {
Log.error("JoseUtil.validateJwt: EC key missing key material", key);
return Promise.reject(new Error("EC key missing key material"));
}
} else {
Log.error("JoseUtil.validateJwt: Unsupported key type", key && key.kty);
return Promise.reject(new Error("Unsupported key type: " + key && key.kty));
}
return JoseUtil._validateJwt(jwt, key, issuer, audience, clockSkew, now, timeInsensitive);
} catch (e) {
Log.error(e && e.message || e);
return Promise.reject("JWT validation failed");
}
}
static validateJwtAttributes(jwt, issuer, audience, clockSkew, now, timeInsensitive) {
if (!clockSkew) {
clockSkew = 0;
}
if (!now) {
now = parseInt(Date.now() / 1000);
}
var payload = JoseUtil.parseJwt(jwt).payload;
if (!payload.iss) {
Log.error("JoseUtil._validateJwt: issuer was not provided");
return Promise.reject(new Error("issuer was not provided"));
}
if (payload.iss !== issuer) {
Log.error("JoseUtil._validateJwt: Invalid issuer in token", payload.iss);
return Promise.reject(new Error("Invalid issuer in token: " + payload.iss));
}
if (!payload.aud) {
Log.error("JoseUtil._validateJwt: aud was not provided");
return Promise.reject(new Error("aud was not provided"));
}
var validAudience = payload.aud === audience || (Array.isArray(payload.aud) && payload.aud.indexOf(audience) >= 0);
if (!validAudience) {
Log.error("JoseUtil._validateJwt: Invalid audience in token", payload.aud);
return Promise.reject(new Error("Invalid audience in token: " + payload.aud));
}
if (payload.azp && payload.azp !== audience) {
Log.error("JoseUtil._validateJwt: Invalid azp in token", payload.azp);
return Promise.reject(new Error("Invalid azp in token: " + payload.azp));
}
if (!timeInsensitive) {
var lowerNow = now + clockSkew;
var upperNow = now - clockSkew;
if (!payload.iat) {
Log.error("JoseUtil._validateJwt: iat was not provided");
return Promise.reject(new Error("iat was not provided"));
}
if (lowerNow < payload.iat) {
Log.error("JoseUtil._validateJwt: iat is in the future", payload.iat);
return Promise.reject(new Error("iat is in the future: " + payload.iat));
}
if (payload.nbf && lowerNow < payload.nbf) {
Log.error("JoseUtil._validateJwt: nbf is in the future", payload.nbf);
return Promise.reject(new Error("nbf is in the future: " + payload.nbf));
}
if (!payload.exp) {
Log.error("JoseUtil._validateJwt: exp was not provided");
return Promise.reject(new Error("exp was not provided"));
}
if (payload.exp < upperNow) {
Log.error("JoseUtil._validateJwt: exp is in the past", payload.exp);
return Promise.reject(new Error("exp is in the past:" + payload.exp));
}
}
return Promise.resolve(payload);
}
static _validateJwt(jwt, key, issuer, audience, clockSkew, now, timeInsensitive) {
return JoseUtil.validateJwtAttributes(jwt, issuer, audience, clockSkew, now, timeInsensitive).then(payload => {
try {
if (!jws.JWS.verify(jwt, key, AllowedSigningAlgs)) {
Log.error("JoseUtil._validateJwt: signature validation failed");
return Promise.reject(new Error("signature validation failed"));
}
return payload;
} catch (e) {
Log.error(e && e.message || e);
return Promise.reject(new Error("signature validation failed"));
}
});
}
static hashString(value, alg) {
try {
return crypto.Util.hashString(value, alg);
} catch (e) {
Log.error(e);
}
}
static hexToBase64Url(value) {
try {
return hextob64u(value);
} catch (e) {
Log.error(e);
}
}
}
}