UNPKG

@sap/xssec

Version:

XS Advanced Container Security API for node.js

130 lines (109 loc) 4.02 kB
'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 */ 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;