@silvermine/apigateway-utils
Version:
Utility functions for working with AWS API Gateway
98 lines (77 loc) • 3.03 kB
JavaScript
'use strict';
var _ = require('underscore'),
jwt = require('jwt-simple'),
Class = require('class.extend'),
APIError = require('./APIError'),
BEARER_REGEX = /^Bearer /,
INVALID_TOKEN_MSG = 'Invalid authorization token';
function createError(title, detail, headerFieldName) {
var err = new APIError(title, detail);
if (headerFieldName) {
err.addSource(APIError.LOC_HEADER, headerFieldName);
}
return err;
}
function invalidFieldResp(field, headerFieldName) {
return createError(INVALID_TOKEN_MSG, 'Invalid "' + field + '" value in the token.', headerFieldName);
}
module.exports = Class.extend({
init: function(publicKey) {
this._publicKey = publicKey;
},
issuer: function(issuer) {
this._issuer = issuer;
return this;
},
audience: function(audience) {
this._audience = audience;
return this;
},
revocation: function(revokedIDs) {
this._revokedIDs = revokedIDs;
return this;
},
validate: function(rawTokenString, isBearerFormat, headerFieldName) {
var errors = [],
tokenString = (isBearerFormat ? (rawTokenString || '').replace(BEARER_REGEX, '') : rawTokenString),
token;
// These first several types of errors must stop the flow of the rest of the
// validation ... they can not be compounded because they result in no token to
// actually validate.
if (_.isEmpty(tokenString)) {
return {
errors: [ createError('No token supplied', undefined, headerFieldName) ],
};
}
if (isBearerFormat && !BEARER_REGEX.test(rawTokenString)) {
return {
// eslint-disable-next-line max-len
errors: [ createError((headerFieldName ? (headerFieldName + ' header') : 'Bearer token') + ' not in correct format', undefined, headerFieldName) ],
};
}
try {
token = jwt.decode(tokenString, this._publicKey, false, 'RS256');
} catch(err) {
return {
errors: [ createError(INVALID_TOKEN_MSG, err.message, headerFieldName) ],
};
}
// The rest of these errors can be built up into an array of errors so that you can
// report all the errors that were found with the decoded token.
if (!_.isEmpty(this._issuer) && token.iss !== this._issuer) {
errors.push(invalidFieldResp('iss', headerFieldName));
}
if (!_.isEmpty(this._audience)) {
if (_.isArray(token.aud) && !_.contains(token.aud, this._audience)) {
errors.push(invalidFieldResp('aud', headerFieldName));
}
if (!_.isArray(token.aud) && token.aud !== this._audience) {
errors.push(invalidFieldResp('aud', headerFieldName));
}
}
if (!_.isEmpty(this._revokedIDs) && _.contains(this._revokedIDs, token.jti)) {
errors.push(createError(INVALID_TOKEN_MSG, 'Token has been revoked', headerFieldName));
}
return { errors: errors, token: token };
},
});