Skip to content
This repository has been archived by the owner on Mar 3, 2022. It is now read-only.

Error classes and exception on validation #915

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
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
31 changes: 20 additions & 11 deletions src/JoseUtilImpl.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.

import { Log } from './Log.js';
import { TokenClockError } from './errors/TokenClockError';
import { TokenExpiredError } from './errors/TokenExpiredError';
import { TokenAttributeInvalidError } from './errors/TokenAttributeInvalidError';
import { TokenAttributeMissingError } from './errors/TokenAttributeMissingError';

export default function getJoseUtil({ jws, KeyUtil, X509, crypto, hextob64u, b64tohex, AllowedSigningAlgs }) {
return class JoseUtil {
Expand Down Expand Up @@ -48,7 +52,12 @@ export default function getJoseUtil({ jws, KeyUtil, X509, crypto, hextob64u, b64
return JoseUtil._validateJwt(jwt, key, issuer, audience, clockSkew, now, timeInsensitive);
} catch (e) {
Log.error(e && e.message || e);
return Promise.reject("JWT validation failed");

if (e instanceof TokenInvalidError === false) {
e = new TokenInvalidError(e && e.message || e);
}

return Promise.reject(e);
}
}

Expand All @@ -65,25 +74,25 @@ export default function getJoseUtil({ jws, KeyUtil, X509, crypto, hextob64u, b64

if (!payload.iss) {
Log.error("JoseUtil._validateJwt: issuer was not provided");
return Promise.reject(new Error("issuer was not provided"));
return Promise.reject(new TokenAttributeMissingError("issuer", "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));
return Promise.reject(new TokenAttributeInvalidError('iss', "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"));
return Promise.reject(new TokenAttributeMissingError("aud", "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));
return Promise.reject(new TokenAttributeInvalidError('aud', "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));
return Promise.reject(new TokenAttributeInvalidError('azp', "Invalid azp in token: " + payload.azp));
}

if (!timeInsensitive) {
Expand All @@ -92,25 +101,25 @@ export default function getJoseUtil({ jws, KeyUtil, X509, crypto, hextob64u, b64

if (!payload.iat) {
Log.error("JoseUtil._validateJwt: iat was not provided");
return Promise.reject(new Error("iat was not provided"));
return Promise.reject(new TokenAttributeMissingError('iat', "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));
return Promise.reject(new TokenClockError("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));
return Promise.reject(new TokenClockError("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"));
return Promise.reject(new TokenAttributeMissingError('exp', "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.reject(new TokenExpiredError("exp is in the past:" + payload.exp));
}
}

Expand Down
13 changes: 13 additions & 0 deletions src/errors/TokenAttributeInvalidError.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { TokenInvalidError } from './TokenInvalidError';

export class TokenAttributeInvalidError extends TokenInvalidError {
constructor(attribute, ...args) {
super(...args);
if (Error.captureStackTrace) {
Error.captureStackTrace(this, TokenAttributeInvalidError);
}

this.name = 'TokenAttributeInvalidError';
this.attribute = attribute;
}
}
12 changes: 12 additions & 0 deletions src/errors/TokenAttributeMissingError.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { TokenInvalidError } from './TokenInvalidError';

export class TokenAttributeMissingError extends TokenInvalidError {
constructor(attribute, ...args) {
super(...args);
if (Error.captureStackTrace) {
Error.captureStackTrace(this, TokenAttributeMissingError);
}
this.name = 'TokenAttributeMissingError';
this.attribute = attribute;
}
}
13 changes: 13 additions & 0 deletions src/errors/TokenClockError.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

import { TokenInvalidError } from './TokenInvalidError';

export class TokenClockError extends TokenInvalidError {
constructor(...args) {
super(...args);
if (Error.captureStackTrace) {
Error.captureStackTrace(this, TokenClockError);
}

this.name = 'TokenClockError';
}
}
13 changes: 13 additions & 0 deletions src/errors/TokenExpiredError.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

import { TokenInvalidError } from './TokenInvalidError';

export class TokenExpiredError extends TokenInvalidError {
constructor(...args) {
super(...args);
if (Error.captureStackTrace) {
Error.captureStackTrace(this, TokenExpiredError);
}

this.name = 'TokenExpiredError';
}
}
14 changes: 14 additions & 0 deletions src/errors/TokenInvalidError.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

/**
* Generic error
*/
export class TokenInvalidError extends Error {
constructor(...args) {
super(...args);
if (Error.captureStackTrace) {
Error.captureStackTrace(this, TokenInvalidError);
}

this.name = 'TokenInvalidError';
}
}
10 changes: 10 additions & 0 deletions test/unit/JoseUtil.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ global.atob = (val) => { return Buffer.from(val, 'base64') };
global.btoa = (val) => { return Buffer.from(val).toString('base64') };

import chai from 'chai';

chai.should();
let expect = chai.expect;

Expand Down Expand Up @@ -159,6 +160,7 @@ let expect = chai.expect;

JoseUtil.validateJwt(jwtFromRsa, rsaKey, expectedIssuer, expectedAudience, 0, notBefore - 1).catch(e => {
e.message.should.contain("iat");
e.name.should.equal('TokenClockError');
done();
});

Expand Down Expand Up @@ -186,6 +188,7 @@ let expect = chai.expect;

JoseUtil.validateJwt(jwtFromRsa, rsaKey, expectedIssuer, expectedAudience, 0, issuedAt - 1).catch(e => {
e.message.should.contain("iat");
e.name.should.equal('TokenClockError');
done();
});

Expand All @@ -204,6 +207,7 @@ let expect = chai.expect;

JoseUtil.validateJwt(jwtFromRsa, rsaKey, expectedIssuer, expectedAudience, 10, issuedAt - 11).catch(e => {
e.message.should.contain("iat");
e.name.should.equal('TokenClockError');
done();
});

Expand All @@ -213,6 +217,7 @@ let expect = chai.expect;

JoseUtil.validateJwt(jwtFromRsa, rsaKey, expectedIssuer, expectedAudience, 0, expires + 1).catch(e => {
e.message.should.contain("exp");
e.name.should.equal('TokenExpiredError');
done();
});

Expand All @@ -231,6 +236,7 @@ let expect = chai.expect;

JoseUtil.validateJwt(jwtFromRsa, rsaKey, expectedIssuer, expectedAudience, 10, expires + 11).catch(e => {
e.message.should.contain("exp");
e.name.should.equal('TokenExpiredError');
done();
});

Expand All @@ -240,6 +246,8 @@ let expect = chai.expect;

JoseUtil.validateJwt(jwtFromRsa, rsaKey, expectedIssuer, "invalid aud", 0, expectedNow).catch(e => {
e.message.should.contain("aud");
e.name.should.equal('TokenAttributeInvalidError');
e.attribute.should.equal('aud');
done();
});
});
Expand All @@ -248,6 +256,8 @@ let expect = chai.expect;

JoseUtil.validateJwt(jwtFromRsa, rsaKey, "invalid issuer", expectedAudience, 0, expectedNow).catch(e => {
e.message.should.contain("issuer");
e.name.should.equal('TokenAttributeInvalidError');
e.attribute.should.equal('iss');
done();
});
});
Expand Down