'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: } ] * - 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); } };