@sap/xssec
Version:
XS Advanced Container Security API for node.js
130 lines (109 loc) • 4.02 kB
JavaScript
;
/*
* Authentication strategy for passport using JSON Web Token (JWT)
*
* If JWT token is present and it is successfully verified, following objects are created:
* - request.user - according to http://passportjs.org/guide/profile/ convention
* - id
* - name
* - givenName
* - familyName
* - emails [ { value: <email> } ]
* - request.authInfo - instance of xssec.SecurityContext
* - getHdbToken(...) - retrieve token for business user to pass to HANA
* - checkScope(...) - authorization checks
* - ...
*
* See http://jwt.io/
* See https://www.npmjs.com/package/passport
*/
const createSecurityContext = require('./createSecurityContextV3');
const { getLogger } = require("../util/logging");
const { FWD_CLIENT_CERT_HEADER } = require('./constantsV3');
const LOG = getLogger("XssecPassportStrategyV3.js");
function JWTStrategy(options, forceType) {
this.options = options;
this.name = 'JWT';
this._forceType = forceType;
}
function SimpleError(errorStr) {
const errobj = new Error(errorStr);
this.getErrorObject = function () {
return errobj;
}
}
JWTStrategy.prototype.authenticate = function (req, passportOptions) {
var authorization = req.headers.authorization;
const authParams = passportOptions;
if (!authorization) {
LOG.debug('Missing Authorization header');
req.tokenInfo = new SimpleError('Missing Authorization header');
return this.fail(401);
}
var parts = authorization.split(' ');
if (parts.length < 2) {
LOG.debug('Invalid Authorization header format');
req.tokenInfo = new SimpleError('Invalid Authorization header format');
return this.fail(400);
}
var scheme = parts[0];
var token = parts[1];
if (scheme.toLowerCase() !== 'bearer') {
LOG.debug('Authorization header is not a Bearer token');
req.tokenInfo = new SimpleError('Authorization header is not a Bearer token');
return this.fail(401);
}
const correlationId = req.headers["x-correlationid"] || req.headers["x-vcap-request-id"];
const x509Certificate = req.headers[FWD_CLIENT_CERT_HEADER];
try {
var self = this;
// eslint-disable-next-line no-inner-declarations
function callback(err, ctx, tokenInfo) {
req.tokenInfo = tokenInfo;
if (err) {
if(!req.tokenInfo) {
req.tokenInfo = new SimpleError(err.toString());
}
return err.statuscode ? self.fail(err.statuscode, err) : self.error(err);
}
if (authParams && authParams.scope) {
var scopes = Array.isArray(authParams.scope) ? authParams.scope : [authParams.scope];
for (var scope of scopes) {
if (!ctx.checkScope(self.options.xsappname + '.' + scope)) {
return self.fail(403);
}
}
}
var jwtLogonName = ctx.getLogonName();
var jwtGivenName = ctx.getGivenName();
var jwtFamilyName = ctx.getFamilyName();
var jwtEmail = ctx.getEmail();
var user = !jwtLogonName ? {} : {
id: jwtLogonName,
name: {
givenName: jwtGivenName,
familyName: jwtFamilyName
},
emails: [{ value: jwtEmail }]
};
// passport will set these in req.user & req.authInfo respectively
self.success(user, ctx);
}
var paramA = this._forceType ? this._forceType : callback;
var paramB = this._forceType ? callback : undefined;
const config = { ...passportOptions, credentials: this.options, correlationId };
if(x509Certificate) {
config.x509Certificate = x509Certificate;
}
createSecurityContext(token, config, paramA, paramB);
}
catch (err) {
LOG.error('JWT verification error: ', err);
this.error(err);
}
};
/**
* Necessary for backwards compatibility: up to version 4.0.4, the JWTStrategy was not exported directly but as a property called JWTStrategy in the export
*/
JWTStrategy.JWTStrategy = JWTStrategy;
module.exports = JWTStrategy;