129 lines
3.7 KiB
JavaScript
129 lines
3.7 KiB
JavaScript
|
'use strict';
|
||
|
|
||
|
/*
|
||
|
* 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
|
||
|
*/
|
||
|
|
||
|
var xssec = require('..');
|
||
|
var debug = require('debug');
|
||
|
var debugTrace = debug('xssec:jwtstrategy');
|
||
|
var debugError = debug('xssec:jwtstrategy');
|
||
|
debugError.log = console.error.bind(console);
|
||
|
debugTrace.log = console.log.bind(console);
|
||
|
|
||
|
const { FWD_CLIENT_CERT_HEADER } = require('../constants');
|
||
|
|
||
|
exports.JWTStrategy = JWTStrategy;
|
||
|
|
||
|
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, options) {
|
||
|
var authorization = req.headers.authorization;
|
||
|
var authParams = options;
|
||
|
|
||
|
if (!authorization) {
|
||
|
debugTrace('Missing Authorization header');
|
||
|
req.tokenInfo = new SimpleError('Missing Authorization header');
|
||
|
return this.fail(401);
|
||
|
}
|
||
|
|
||
|
var parts = authorization.split(' ');
|
||
|
if (parts.length < 2) {
|
||
|
debugTrace('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') {
|
||
|
debugTrace('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 {
|
||
|
|
||
|
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 self = this;
|
||
|
var paramA = this._forceType ? this._forceType : callback;
|
||
|
var paramB = this._forceType ? callback : undefined;
|
||
|
const config = { ...options, credentials: this.options, correlationId };
|
||
|
if(x509Certificate) {
|
||
|
config.x509Certificate = x509Certificate;
|
||
|
}
|
||
|
|
||
|
xssec.createSecurityContext(token, config, paramA, paramB);
|
||
|
}
|
||
|
catch (err) {
|
||
|
debugError('JWT verification error: ', err);
|
||
|
this.error(err);
|
||
|
}
|
||
|
};
|