Socket
Socket
Sign inDemoInstall

@sap/xssec

Package Overview
Dependencies
Maintainers
3
Versions
82
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@sap/xssec - npm Package Compare versions

Comparing version 2.2.4 to 3.0.0

lib/requests.js

13

CHANGELOG.md
# Change Log
All notable changes to this project will be documented in this file.
## 3.0.0 - 2020-??-??
- Replace grant type user_token in method requestToken (TYPE_USER_TOKEN) in favor of urn:ietf:params:oauth:grant-type:jwt-bearer
- Remove obsolete method getToken (use getHdbToken or getAppToken))
- Remove obsolete method requestTokenForClient (use requestToken)
- Remove obsolete method getIdentityZone (getSubaccountId)
## 2.2.5 - 2020-02-28
- Update to node-jwt version 1.6.6
## 2.2.4 - 2019-08-14
- Support for API methods getUserName and getUniquePrincipalName
- Support for API methods getUserName and getUniquePrincipalName

@@ -8,0 +19,0 @@ ## 2.2.3 - 2019-08-07

1

lib/index.js

@@ -6,5 +6,4 @@ 'use strict';

exports.createSecurityContextCC = xssec.createSecurityContextCC;
exports.createSecurityContext = xssec.createSecurityContext;
exports.constants = require('./constants');
exports.JWTStrategy = passportStrategy.JWTStrategy;
'use strict';
const constants = require('./constants');
const request = require('request');
const url = require('url');
var constants = require('./constants');
var request = require('request');
var url = require('url');
const requests = require('./requests');

@@ -12,518 +13,202 @@ // use environment variable DEBUG with value 'xssec:*' for trace/error messages

var keycache = require('./keycache');
const JwtTokenValidator = require('./validator').JwtTokenValidator;
const VerificationKey = require('./verificationkey');
// Note: the keycache is initialized currently with the default size defined in constants
// Consider making this configurable for the application, e.g. via xssecurity.json
// or (probably worse) via environment variables.
// Similarly, the keycache uses the default expiration time for cache entries as
// defined in constants. Also here, consider making this configurable.
var keyCache = new keycache.KeyCache(constants.KEYCACHE_DEFAULT_CACHE_SIZE, constants.KEYCACHE_DEFAULT_CACHE_ENTRY_EXPIRATION_TIME_IN_MINUTES);
debugError.log = console.error.bind(console);
debugTrace.log = console.log.bind(console);
exports.createSecurityContext = createSecurityContext;
function throw500(errorString) {
debugError('\n' + errorString);
var error = new Error(errorString);
error.statuscode = 500; //500 (Invalid config)
throw error;
}
function createSecurityContext(token, config, cb) {
var securityContext;
//For Backward compatibilty
exports.createSecurityContext = function (token, config, cb) {
try {
securityContext = new SecurityContext(token, config);
securityContext.init(done);
var securityContext = new SecurityContext(config);
securityContext.verifyToken(token, cb);
} catch (e) {
cb(e);
}
function done(err) {
if (err) {
return cb(err);
}
cb(null, securityContext);
}
}
function SecurityContext(token, config) {
this.token = token;
this.config = config;
this.xsappname = '';
this.isForeignMode = false;
this.tokenContainsAttributes = false;
this.tokenContainsAdditionalAuthAttributes = false;
this.userInfo = {
logonName : '',
givenName : '',
familyName : '',
email : ''
function SecurityContext(config) {
var userInfo = {
logonName: '',
givenName: '',
familyName: '',
email: ''
};
this.scopes = [];
this.samlToken = '';
this.clientId = '';
this.identityZone = '';
this.subdomain = null;
this.origin = null;
this.userAttributes = '';
this.additionalAuthAttributes = '';
this.serviceinstanceid = null;
this.grantType = '';
// sapssoext
var ssojwt;
if (process.sapnodejwtlib) {
this.ssojwt = process.sapnodejwtlib;
} else {
try {
var jwt = require('@sap/node-jwt');
this.ssojwt = process.sapnodejwtlib = new jwt("");
} catch (e) {
var error = new Error('No jwt.node available. Error: ' + e.message );
error.statuscode = 500; //No jwt.node
throw error;
}
}
var token;
var xsappname;
var scopes;
var samlToken;
var clientId;
var identityZone;
var subdomain = null;
var origin = null;
var userAttributes;
var additionalAuthAttributes;
var serviceinstanceid = null;
var grantType;
var expirationDate;
// validate config input
debugTrace('\nConfiguration (note: clientsecret might be contained but is not traced): ' + JSON.stringify(config, credentialsReplacer, 4));
if (!this.ssojwt || this.ssojwt.getLibraryVersion() === -1) {
debugTrace('\nSSO library path: ' + process.env['SSOEXT_LIB']);
var error = new Error('JWT validation library could not be loaded. Used '
+ process.env['SSOEXT_LIB']);
error.statuscode = 500; //lib not found
throw error;
}
var isForeignMode = false;
// validate config input
if (!token || token.length === 0) {
var error = new Error('Invalid token (empty).');
error.statuscode = 401; //(Token Empty/No Token)
throw error;
}
if (!config) {
var error = new Error('Invalid config (missing).');
error.statuscode = 500; //500 (Invalid config)
throw error;
}
if (!this.config.clientid) {
var error = new Error('Invalid config: Missing clientid.');
error.statuscode = 500; //500 (Invalid config)
throw error;
}
if (!this.config.clientsecret) {
var error = new Error('Invalid config: Missing clientsecret.');
error.statuscode = 500; //500 (Invalid config)
throw error;
}
if (!this.config.url) {
var error = new Error('Invalid config: Missing url.');
error.statuscode = 500; //500 (Invalid config)
throw error;
}
if (!this.config.xsappname) {
if (!process.env.XSAPPNAME) {
var errorString = 'Invalid config: Missing xsappname.\n'
function validateXSAPPNAME() {
if (!config.xsappname) {
if (!process.env.XSAPPNAME) {
var errorString = 'Invalid config: Missing xsappname.\n'
+ 'The application name needs to be defined in xs-security.json.';
debugError('\n' + errorString);
var error = new Error(errorString);
error.statuscode = 500; //500 (Invalid config)
throw error;
} else {
this.xsappname = process.env.XSAPPNAME;
debugTrace('\nXSAPPNAME defined in manifest.yml (legacy).\n'
return throw500(errorString);
} else {
xsappname = process.env.XSAPPNAME;
debugTrace('\nXSAPPNAME defined in manifest.yml (legacy).\n'
+ 'You should switch to defining xsappname in xs-security.json.');
}
} else {
if (!process.env.XSAPPNAME) {
this.xsappname = this.config.xsappname;
}
} else {
if (process.env.XSAPPNAME == this.config.xsappname) {
this.xsappname = process.env.XSAPPNAME;
debugTrace('\nThe application name is defined both in the manifest.yml (legacy) \n'
if (!process.env.XSAPPNAME) {
xsappname = config.xsappname;
} else {
if (process.env.XSAPPNAME == config.xsappname) {
xsappname = process.env.XSAPPNAME;
debugTrace('\nThe application name is defined both in the manifest.yml (legacy) \n'
+ 'as well as in xs-security.json. Remove it in manifest.yml.');
} else {
var errorString = 'Invalid config: Ambiguous xsappname.\n'
} else {
var errorString = 'Invalid config: Ambiguous xsappname.\n'
+ 'The application name is defined with different values in the manifest.yml (legacy)\n'
+ 'as well as in xs-security.json. Remove it in manifest.yml.';
debugError('\n' + errorString);
var error = new Error(errorString);
error.statuscode = 500; //500 (Invalid config)
throw error;
return throw500(errorString);
}
}
}
}
}
SecurityContext.prototype.init = function init(cb) {
var self = this;
function ctor() {
// validate config input
debugTrace('\nConfiguration (note: clientsecret might be contained but is not traced): ' +
JSON.stringify(config, function(key, value) {
return key === 'clientsecret' ? undefined : value;
}, 4));
offlineValidation(
self.token,
self.config,
self.ssojwt,
function(error, result) {
if (error) {
debugError(error.message);
debugError(error.stack);
return cb(error, null);
}
// validate config input
if (!config) {
return throw500('Invalid config (missing).');
}
if (!result.cid) {
var errorString = 'Client Id not contained in access token. Giving up!';
debugError('\n' + errorString);
var error = new Error(errorString);
error.statuscode = 400; //400 (no clientID)
return cb(error, null);
}
if (!result.zid) {
var errorString = 'Identity Zone not contained in access token. Giving up!';
debugError('\n' + errorString);
var error = new Error(errorString);
error.statuscode = 400; //400 (wrong idz)
return cb(error, null);
}
validateXSAPPNAME();
var subscriptionsAllowed = false;
if (result.cid.indexOf('!t') !== -1 || result.cid.indexOf('!b') !== -1) {
subscriptionsAllowed = true;
}
if ((result.cid === self.config.clientid)
&& (result.zid === self.config.identityzoneid || subscriptionsAllowed === true)) {
// clientid and identity zone of the token must match with the values of the application
// (if the token was issued for the xsuaa application or broker plan it is sufficient that the clientid matches)
if (subscriptionsAllowed === false) {
debugTrace('\nClient Id and Identity Zone of the access token match\n'
+ 'with the current application\'s Client Id and Zone.');
} else {
debugTrace('\nClient Id of the access token (XSUAA application or broker plan) matches\n'
+ 'with the current application\'s Client Id.');
}
self.isForeignMode = false;
} else if (self.config.trustedclientidsuffix && self.config.trustedclientidsuffix.length > 0 &&
result.cid.substring(result.cid.length - self.config.trustedclientidsuffix.length, result.cid.length) === self.config.trustedclientidsuffix) {
debugTrace('\nClient Id "' + result.cid + '" of the access token allows consumption by the Client Id "' +
self.config.clientid + '" of the current application\n');
self.isForeignMode = false;
} else if (process.env.SAP_JWT_TRUST_ACL) {
debugTrace('\nClient Id "'
+ result.cid
+ '" and/or Identity Zone "'
+ result.zid
+ '" of the access token\n'
+ 'does/do not match with the Client Id "'
+ self.config.clientid
+ '" and Identity Zone "'
+ self.config.identityzoneid
+ '"\nof the current application.\n'
+ 'Validating token against JWT trust ACL (SAP_JWT_TRUST_ACL).');
var parsedACL;
try {
parsedACL = JSON.parse(process.env.SAP_JWT_TRUST_ACL);
} catch (er) {
var errorString = 'JWT trust ACL (ACL SAP_JWT_TRUST_ACL):\n'
+ process.env.SAP_JWT_TRUST_ACL
+ '\ncould not be parsed successfully.\n'
+ 'Error: ' + er.message;
debugError('\n' + errorString);
var error = new Error(errorString);
error.statuscode = 500; //500 (Internal Server Error)
return cb(error, null);
}
var foundMatch = false;
for ( var aclEntry in parsedACL) {
if (((result.cid === parsedACL[aclEntry].clientid) || ('*' === parsedACL[aclEntry].clientid))
&& ((result.zid === parsedACL[aclEntry].identityzone) || ('*' === parsedACL[aclEntry].identityzone))) {
foundMatch = true;
break;
}
}
if (foundMatch) {
debugTrace('\nForeign token received, but matching entry\n'
+ 'in JWT trust ACL (SAP_JWT_TRUST_ACL) found.');
self.isForeignMode = true;
} else {
var errorString = 'Client Id "'
+ result.cid
+ '" and/or Identity Zone "'
+ result.zid
+ '" of the access token\n'
+ 'does/do not match with the Client Id "'
+ self.config.clientid
+ '" and Identity Zone "'
+ self.config.identityzoneid
+ '" of the current application.\n'
+ 'No match found in JWT trust ACL (SAP_JWT_TRUST_ACL):\n'
+ JSON.stringify(parsedACL, null, 4);
debugError('\n' + errorString);
var error = new Error(errorString);
error.statuscode = 403; //403 Forbidden (as no JWT trust entry)
return cb(error, null);
}
} else {
if (result.cid !== self.config.clientid) {
var errorString = 'Client Id of the access token "'
+ result.cid
+ '" does not match with\nthe OAuth Client Id "'
+ self.config.clientid
+ '" of the application.\n'
+ 'No JWT trust ACL (SAP_JWT_TRUST_ACL) specified in environment.';
debugError('\n' + errorString);
var error = new Error(errorString);
error.statuscode = 403; //403 Forbidden (as client IDs do not match and no trust entry exists)
return cb(error, null);
} else {
var errorString = 'Identity Zone of the access token "'
+ result.zid
+ '" does not match\nwith the Identity Zone "'
+ self.config.identityzoneid
+ '" of the application.\n'
+ 'No JWT trust ACL (SAP_JWT_TRUST_ACL) specified in environment.';
debugError('\n' + errorString);
var error = new Error(errorString);
error.statuscode = 403; //403 Forbidden (as identity zones do not match and no trust entry exists)
return cb(error, null);
}
}
debugTrace('\nApplication received a token of grant type "' + result.grant_type + '".');
self.identityZone = result.zid;
self.clientId = result.cid;
if (result.origin !== null && result.origin !== undefined) {
self.origin = result.origin;
}
self.expirationDate = new Date(result.exp * 1000);
self.grantType = result.grant_type;
if (self.grantType !== constants.GRANTTYPE_CLIENTCREDENTIAL) {
self.userInfo.logonName = result.user_name; // jshint ignore:line
if (result.hasOwnProperty('ext_attr') && result.ext_attr.given_name !== undefined) {
self.userInfo.givenName = result.ext_attr.given_name;
} else {
self.userInfo.givenName = result.given_name; // jshint ignore:line
}
if (result.hasOwnProperty('ext_attr') && result.ext_attr.family_name !== undefined) {
self.userInfo.familyName = result.ext_attr.family_name;
} else {
self.userInfo.familyName = result.family_name; // jshint ignore:line
}
self.userInfo.email = result.email;
debugTrace('\nObtained logon name: ' + self.userInfo.logonName);
debugTrace('Obtained given name: ' + self.userInfo.givenName);
debugTrace('Obtained family name: ' + self.userInfo.familyName);
debugTrace('Obtained email: ' + self.userInfo.email);
if (result.hasOwnProperty('ext_cxt')) {
if (result.ext_cxt['hdb.nameduser.saml'] !== undefined) {
self.samlToken = result.ext_cxt['hdb.nameduser.saml'];
}
if (result.ext_cxt['xs.user.attributes'] !== undefined) {
self.userAttributes = result.ext_cxt['xs.user.attributes'];
self.tokenContainsAttributes = true;
debugTrace('\nObtained attributes: '
+ JSON.stringify(self.userAttributes, null, 4));
} else {
self.tokenContainsAttributes = false;
debugTrace('\nObtained attributes: no XS user attributes in JWT token available.');
}
if (!config.clientid) {
return throw500('Invalid config: Missing clientid.');
}
if (!config.clientsecret) {
return throw500('Invalid config: Missing clientsecret.');
}
if (!config.url) {
return throw500('Invalid config: Missing url.');
}
}
} else {
self.samlToken = result['hdb.nameduser.saml'];
if (result.hasOwnProperty('xs.user.attributes')) {
self.userAttributes = result['xs.user.attributes'];
self.tokenContainsAttributes = true;
debugTrace('\nObtained attributes: '
+ JSON.stringify(self.userAttributes, null, 4));
} else {
self.tokenContainsAttributes = false;
debugTrace('\nObtained attributes: no XS user attributes in JWT token available.');
}
}
}
if (result.hasOwnProperty('az_attr')) {
self.additionalAuthAttributes = result['az_attr'];
self.tokenContainsAdditionalAuthAttributes = true;
debugTrace('\nObtained additional authentication attributes: '
+ JSON.stringify(self.additionalAuthAttributes, null, 4));
} else {
self.tokenContainsAdditionalAuthAttributes = false;
debugTrace('\nObtained attributes: no additional authentication attributes in JWT token available.');
}
if (result.hasOwnProperty('ext_attr') && result.ext_attr.serviceinstanceid !== undefined) {
self.serviceinstanceid = result.ext_attr.serviceinstanceid;
}
if (result.hasOwnProperty('ext_attr') && result.ext_attr.zdn !== undefined) {
self.subdomain = result.ext_attr.zdn;
}
debugTrace('\nObtained subdomain: ' + self.subdomain);
debugTrace('Obtained serviceinstanceid: ' + self.serviceinstanceid);
debugTrace('Obtained origin: ' + self.origin);
self.scopes = result.scope;
debugTrace('Obtained scopes: '
+ JSON.stringify(self.scopes, null, 4));
cb();
});
};
function ifNotClientCredentialsToken(functionName, value) {
if (grantType === constants.GRANTTYPE_CLIENTCREDENTIAL) {
var errorString = '\nCall to ' + functionName + ' not allowed with a token of grant type ' + constants.GRANTTYPE_CLIENTCREDENTIAL + '.';
debugTrace(errorString);
return null;
}
return value;
}
SecurityContext.prototype.getIdentityZone = function() {
return this.identityZone;
};
this.getSubaccountId = function () {
return identityZone;
};
SecurityContext.prototype.getSubaccountId = function() {
return this.identityZone;
};
this.getSubdomain = function () {
return subdomain;
};
SecurityContext.prototype.getSubdomain = function() {
return this.subdomain;
};
this.getClientId = function () {
return clientId;
};
SecurityContext.prototype.getClientId = function() {
return this.clientId;
};
this.getExpirationDate = function () {
return expirationDate;
};
SecurityContext.prototype.getExpirationDate = function() {
return this.expirationDate;
};
this.getOrigin = function () {
return origin;
};
SecurityContext.prototype.getOrigin = function() {
return this.origin;
};
this.getLogonName = function () {
return ifNotClientCredentialsToken('SecurityContext.getLogonName', userInfo.logonName);
};
SecurityContext.prototype.getLogonName = function() {
try {
forbidClientCredentialsToken('SecurityContext.getLogonName', this.grantType);
} catch (e) {
return null;
}
return this.userInfo.logonName;
};
this.getGivenName = function () {
return ifNotClientCredentialsToken('SecurityContext.getGivenName', userInfo.givenName);
};
SecurityContext.prototype.getGivenName = function() {
try {
forbidClientCredentialsToken('SecurityContext.getGivenName', this.grantType);
} catch (e) {
return null;
}
return this.userInfo.givenName;
};
this.getFamilyName = function () {
return ifNotClientCredentialsToken('SecurityContext.getFamilyName', userInfo.familyName);
};
SecurityContext.prototype.getFamilyName = function() {
try {
forbidClientCredentialsToken('SecurityContext.getFamilyName', this.grantType);
} catch (e) {
return null;
}
return this.userInfo.familyName;
};
this.getEmail = function () {
return ifNotClientCredentialsToken('SecurityContext.getEmail', userInfo.email);
};
SecurityContext.prototype.getEmail = function() {
try {
forbidClientCredentialsToken('SecurityContext.getEmail', this.grantType);
} catch (e) {
return null;
}
return this.userInfo.email;
};
this.getUserName = function () {
if (grantType === constants.GRANTTYPE_CLIENTCREDENTIAL) {
return `client/${clientId}`;
} else {
return this.getUniquePrincipalName(origin, userInfo.logonName);
}
};
SecurityContext.prototype.getUserName = function() {
if (this.grantType === constants.GRANTTYPE_CLIENTCREDENTIAL) {
return `client/${this.clientId}`;
} else {
return this.getUniquePrincipalName(this.origin, this.userInfo.logonName);
}
};
this.getUniquePrincipalName = function (origin, logonName) {
if(!ifNotClientCredentialsToken('SecurityContext.getUniquePrincipalName', true)) {
return null;
}
if (!origin) {
debugTrace('Origin claim not set in JWT. Cannot create unique user name. Returning null.');
return null;
}
if (!logonName) {
debugTrace('User login name claim not set in JWT. Cannot create unique user name. Returning null.');
return null;
}
if (origin.includes('/')) {
debugTrace('Illegal \'/\' character detected in origin claim of JWT. Cannot create unique user name. Retuning null.');
return null;
}
return `user/${origin}/${logonName}`;
};
SecurityContext.prototype.getUniquePrincipalName = function(origin, logonName) {
var errorString;
try {
forbidClientCredentialsToken('SecurityContext.getUniquePrincipalName', this.grantType);
} catch (e) {
return null;
}
if (!origin) {
errorString = 'Origin claim not set in JWT. Cannot create unique user name. Returning null.';
debugTrace(errorString);
return null;
}
if (!logonName) {
errorString = 'User login name claim not set in JWT. Cannot create unique user name. Returning null.';
debugTrace(errorString);
return null;
}
if (origin.includes('/')) {
errorString = 'Illegal \'/\' character detected in origin claim of JWT. Cannot create unique user name. Retuning null.';
debugTrace(errorString);
return null;
}
return `user/${origin}/${logonName}`;
};
SecurityContext.prototype.getToken = function(namespace, name) {
if (this.tokenContainsAttributes && this.isForeignMode) {
debugTrace('\nThe SecurityContext has been initialized with an access token of a\n'
this.getHdbToken = function () {
if (userAttributes && isForeignMode) {
debugTrace('\nThe SecurityContext has been initialized with an access token of a\n'
+ 'foreign OAuth Client Id and/or Identity Zone. Furthermore, the \n'
+ 'access token contains attributes. Due to the fact that we want to\n'
+ 'restrict attribute access to the application that provided the \n'
+ 'attributes, the getToken function does not return a valid token.\n');
return null;
}
if (namespace === undefined || namespace === null) {
debugTrace('\nInvalid token namespace (may not be null or undefined).');
return null;
} else if (namespace !== constants.SYSTEM) {
debugTrace('\nNamespace "' + namespace + '" not supported.');
return null;
}
if (name === undefined || name === null) {
debugTrace('\nInvalid token name (may not be null or undefined).');
return null;
}
switch (name) {
case constants.JOBSCHEDULER:
return this.token;
case constants.HDB:
if (this.samlToken === undefined || this.samlToken === null) {
return this.token;
} else {
return this.samlToken;
+ 'attributes, the getHdbToken function does not return a valid token.\n');
return null;
}
default:
debugTrace('\nToken name "' + name + '" not supported.');
return null;
}
};
SecurityContext.prototype.getHdbToken = function() {
return this.getToken(constants.SYSTEM, constants.HDB);
};
return samlToken ? samlToken : token;
};
SecurityContext.prototype.getAppToken = function() {
return this.token;
};
this.getAppToken = function () {
return token;
};
SecurityContext.prototype.requestTokenForClient = function(serviceCredentials, scopes, cb) {
return requestUserToken(this, serviceCredentials, null, scopes, false, cb);
};
SecurityContext.prototype.requestToken = function(serviceCredentials, type, additionalAttributes, cb) {
if (type === constants.TYPE_USER_TOKEN) {
return requestUserToken(this, serviceCredentials, additionalAttributes, null, true, cb);
} else if (type === constants.TYPE_CLIENT_CREDENTIALS_TOKEN) {
return requestClientCredentialsToken(this, serviceCredentials, additionalAttributes, cb);
} else {
var error = new Error('Invalid grant type.');
return cb(error, null);
}
};
SecurityContext.prototype.getAttribute = function(name) {
try {
forbidClientCredentialsToken('SecurityContext.getAttribute', this.grantType);
} catch (e) {
return null;
}
if (!this.tokenContainsAttributes) {
debugTrace('\nThe access token contains no user attributes.\n');
return null;
}
if (this.isForeignMode) {
debugTrace('\nThe SecurityContext has been initialized with an access token of a\n'
this.getAttribute = function (name) {
if(!ifNotClientCredentialsToken('SecurityContext.getAttribute', true)) {
return null;
}
if (!userAttributes) {
debugTrace('\nThe access token contains no user attributes.\n');
return null;
}
if (isForeignMode) {
debugTrace('\nThe SecurityContext has been initialized with an access token of a\n'
+ 'foreign OAuth Client Id and/or Identity Zone. Furthermore, the \n'

@@ -533,391 +218,159 @@ + 'access token contains attributes. Due to the fact that we want to\n'

+ 'attributes, the getAttribute function does not return any attributes.\n');
return null;
}
if (name === undefined || name === null || name === '') {
debugTrace('\nInvalid attribute name (may not be null, empty, or undefined).');
return null;
}
if (!this.userAttributes.hasOwnProperty(name)) {
debugTrace('\nNo attribute "' + name + '" found for user "'
+ this.userInfo.logonName + '".');
return null;
}
return this.userAttributes[name];
};
return null;
}
if (!name) {
debugTrace('\nInvalid attribute name (may not be null, empty, or undefined).');
return null;
}
if (!userAttributes[name]) {
debugTrace('\nNo attribute "' + name + '" found for user "' + this.getLogonName() + '".');
return null;
}
return userAttributes[name];
};
SecurityContext.prototype.getAdditionalAuthAttribute = function(name) {
if (!this.tokenContainsAdditionalAuthAttributes) {
debugTrace('\nThe access token contains no additional authentication attributes.\n');
return null;
}
if (name === undefined || name === null || name == '') {
debugTrace('\nInvalid attribute name (may not be null, empty, or undefined).');
return null;
}
if (!this.additionalAuthAttributes.hasOwnProperty(name)) {
debugTrace('\nNo attribute "' + name + '" found as additional authentication attribute.');
return null;
}
return this.additionalAuthAttributes[name];
};
this.getAdditionalAuthAttribute = function (name) {
if (!additionalAuthAttributes) {
debugTrace('\nThe access token contains no additional authentication attributes.\n');
return null;
}
if (!name) {
debugTrace('\nInvalid attribute name (may not be null, empty, or undefined).');
return null;
}
if (!additionalAuthAttributes[name]) {
debugTrace('\nNo attribute "' + name + '" found as additional authentication attribute.');
return null;
}
return additionalAuthAttributes[name];
};
SecurityContext.prototype.getCloneServiceInstanceId = function() {
return this.serviceinstanceid;
};
this.getCloneServiceInstanceId = function () {
return serviceinstanceid;
};
SecurityContext.prototype.isInForeignMode = function() {
return this.isForeignMode;
};
this.isInForeignMode = function () {
return isForeignMode;
};
SecurityContext.prototype.hasAttributes = function() {
try {
forbidClientCredentialsToken('SecurityContext.hasAttributes', this.grantType);
} catch (e) {
return null;
}
return this.tokenContainsAttributes;
};
this.hasAttributes = function () {
return ifNotClientCredentialsToken('SecurityContext.hasAttributes', userAttributes ? true : false);
};
SecurityContext.prototype.checkLocalScope = function(scope) {
var scopeName = this.xsappname + '.' + scope;
if ((scope === null) || (scope === undefined)) {
return false;
}
return this.scopes.indexOf(scopeName) !== -1;
};
this.checkLocalScope = function (scope) {
if (!scope) {
return false;
}
var scopeName = xsappname + '.' + scope;
return scopes.indexOf(scopeName) !== -1;
};
SecurityContext.prototype.getGrantType = function() {
return this.grantType;
};
this.getGrantType = function () {
return grantType;
};
SecurityContext.prototype.checkScope = function(scope) {
var scopeName = scope;
this.checkScope = function (scope) {
if (!scope) {
return false;
}
if ((scope === null) || (scope === undefined)) {
return false;
}
if (scopeName.substring(0, constants.XSAPPNAMEPREFIX.length) === constants.XSAPPNAMEPREFIX) {
scopeName = scopeName.replace(constants.XSAPPNAMEPREFIX, this.xsappname + '.');
}
return this.scopes.indexOf(scopeName) !== -1;
};
function loadVerificationKey(accessToken, config, cb) {
if (config.verificationkey === undefined) {
var error = new Error('Error in offline validation of access token, because of missing verificationkey', null);
error.statuscode = 500; //500 (missing verificationkey)
return cb(error);
}
var invalidatedTokenHeaderJSON = null;
try {
var invalidatedTokenParts = accessToken.split('.');
if (invalidatedTokenParts.length !== 3) {
var error = new Error('Unexpected JWT structure.', null);
error.statuscode = 400;
return cb(error);
if (scope.substring(0, constants.XSAPPNAMEPREFIX.length) === constants.XSAPPNAMEPREFIX) {
scope = scope.replace(constants.XSAPPNAMEPREFIX, xsappname + '.');
}
var invalidatedTokenHeaderBuffer = Buffer.from(invalidatedTokenParts[0], 'base64');
var invalidatedTokenHeaderString = invalidatedTokenHeaderBuffer.toString('utf8');
invalidatedTokenHeaderJSON = JSON.parse(invalidatedTokenHeaderString);
if (!invalidatedTokenHeaderJSON.kid || invalidatedTokenHeaderJSON.kid == 'legacy-token-key' || !invalidatedTokenHeaderJSON.jku) {
return cb(null, config.verificationkey);
}
validateJku(invalidatedTokenHeaderJSON.jku, config.uaadomain, function(err) {
if (err) {
return cb(null, config.verificationkey);
}
keyCache.getKey(invalidatedTokenHeaderJSON.jku, invalidatedTokenHeaderJSON.kid, function(err, key) {
if (err) {
return cb(null, config.verificationkey);
} else {
return cb(null, key);
}
});
});
} catch (e) {
e.statuscode = 403;
return cb(e);
}
}
return scopes.indexOf(scope) !== -1;
};
function validateJku(jkuUrl, uaaDomain, cb) {
if (uaaDomain === null || uaaDomain === undefined) {
var errorString = 'Service is not properly configured in \'VCAP_SERVICES\', attribute \'uaadomain\' is missing. Use legacy-token-key.';
debugTrace('\n' + errorString);
return cb(new Error(errorString));
}
var tokenKeyUrl = url.parse(jkuUrl);
if (tokenKeyUrl.hostname.substring(tokenKeyUrl.hostname.indexOf(uaaDomain), tokenKeyUrl.hostname.length) !== uaaDomain) {
var errorString = 'JKU of the JWT token (' + jkuUrl + ') does not match with the uaa domain (' + uaaDomain + '). Use legacy-token-key.';
debugTrace('\n' + errorString);
return cb(new Error(errorString));
}
cb(null);
}
function checkTokenLocal(accessToken, verificationkey, ssojwt, cb) {
var ssorc = ssojwt.loadPEM(verificationkey);
if ((ssorc !== 0) && (ssorc === 9)) {
debugTrace('\nSSO library path: ' + process.env['SSOEXT_LIB']);
debugTrace('\nCCL library path: ' + process.env['SSF_LIB']);
debugTrace('\nSSO library version: ' + ssojwt.getLibraryVersion());
debugTrace('\nSSO library code: ' + ssojwt.getErrorRC());
var error = new Error('Error in sapssoext, version : '
+ ssojwt.getLibraryVersion() + ' . Cannot load CCL from path: '
+ process.env['SSF_LIB'], null);
error.statuscode = 500; //500 (lib not found)
return cb(error);
}
ssojwt.checkToken(accessToken);
if (ssojwt.getErrorDescription() !== "") {
ssorc = ssojwt.getErrorRC();
debugTrace('\nSSO library path: ' + process.env['SSOEXT_LIB']);
debugTrace('\nCCL library path: ' + process.env['SSF_LIB']);
debugTrace('\nSSO library version: ' + ssojwt.getLibraryVersion());
debugTrace('\nSSO library code: ' + ssojwt.getErrorRC());
if ((ssorc !== 0) && (ssorc === 5)) {
// verification key and JWT are not valid, no library error
debugTrace('\nInvalid JWT: ' + accessToken);
var error = new Error(
'Invalid access token. Validation error: '
+ ssojwt.getErrorDescription(), null);
error.statuscode = 403; //403 (validation error)
return cb(error);
this.requestToken = function (serviceCredentials, type, additionalAttributes, cb) {
if (type === constants.TYPE_USER_TOKEN) {
return requests.requestUserToken(this, serviceCredentials, additionalAttributes, null, true, cb);
} else if (type === constants.TYPE_CLIENT_CREDENTIALS_TOKEN) {
return requests.requestClientCredentialsToken(this, serviceCredentials, additionalAttributes, cb);
} else {
var error = new Error(
'Error in offline validation of access token: '
+ ssojwt.getErrorDescription(), null);
error.statuscode = 403; //403 (validation error)
return cb(error);
return cb(new Error('Invalid grant type.'));
}
}
var parsedPayload = null;
try {
parsedPayload = JSON.parse(ssojwt.getJWPayload());
} catch (er) {
var errorString = 'Access token payload could not be parsed successfully.\n'
+ 'Error: ' + er.message;
debugError('\n' + errorString);
var error = new Error(errorString);
error.statuscode = 400; //400 (parsing error)
return cb(error, null);
}
cb(null, parsedPayload);
}
};
function offlineValidation(accessToken, config, ssojwt, cb) {
loadVerificationKey(accessToken, config, function(err, verificationkey) {
if (err) {
return cb(err);
}
checkTokenLocal(accessToken, verificationkey, ssojwt, cb);
});
}
function fillContext(encodedToken, decodedToken) {
debugTrace('\nApplication received a token of grant type "' + decodedToken.grant_type + '".');
function credentialsReplacer(key, value) {
if (key === 'clientsecret') {
return undefined;
} else {
return value;
}
}
token = encodedToken;
scopes = decodedToken.scope;
identityZone = decodedToken.zid;
clientId = decodedToken.cid;
expirationDate = new Date(decodedToken.exp * 1000);
grantType = decodedToken.grant_type;
function requestUserToken(securityContext, serviceCredentials, additionalAttributes, scopes, adaptSubdomain, cb) {
// input validation
if (!serviceCredentials) {
var error = new Error('Parameter serviceCredentials is missing but mandatory.');
return cb(error, null);
}
if (!serviceCredentials.clientid || !serviceCredentials.clientsecret) {
var error = new Error('Invalid service credentials: Missing clientid/clientsecret.');
return cb(error, null);
}
if (!serviceCredentials.url) {
var error = new Error('Invalid service credentials: Missing url.');
return cb(error, null);
}
if (securityContext.checkScope('uaa.user') === false) {
var error = new Error('JWT token does not include scope "uaa.user".');
return cb(error, null);
}
// adapt subdomain in service url, if necessary
var urlWithCorrectSubdomain = serviceCredentials.url;
if (adaptSubdomain === true) {
var tokenSubdomain = securityContext.getSubdomain();
var tokenRequestSubdomain = null;
var uaaUrl = url.parse(serviceCredentials.url);
if (uaaUrl.hostname.indexOf('.') === -1) {
tokenRequestSubdomain = null;
} else {
tokenRequestSubdomain = uaaUrl.hostname.substring(0, uaaUrl.hostname.indexOf('.'));
}
if (tokenSubdomain !== null && tokenRequestSubdomain != null && tokenSubdomain !== tokenRequestSubdomain) {
urlWithCorrectSubdomain = uaaUrl.protocol + "//" + tokenSubdomain + uaaUrl.host.substring(uaaUrl.host.indexOf('.'), uaaUrl.host.size);
}
}
// user token flow
var options = {
url : urlWithCorrectSubdomain + '/oauth/token?grant_type=user_token&response_type=token&client_id=' + serviceCredentials.clientid,
headers: { Accept: 'application/json' },
auth: {
bearer: securityContext.token
},
timeout: 2000
};
if (scopes !== null) {
options.url = options.url + "&scope=" + scopes;
}
if (additionalAttributes !== null) {
var authorities = { "az_attr" : additionalAttributes };
options.url = options.url + "&authorities=" + encodeURIComponent(JSON.stringify(authorities));
}
request.post(
options,
function(error, response, body) {
if (error) {
if (error.code === 'ETIMEDOUT' && error.connect === true) {
debugError('requestToken: HTTP connection timeout.');
}
debugError(error.message);
debugError(error.stack);
return cb(error, null);
origin = decodedToken.origin || null;
if (grantType !== constants.GRANTTYPE_CLIENTCREDENTIAL) {
var givenName, familyName;
if(decodedToken.ext_attr) {
givenName = decodedToken.ext_attr.given_name || null;
familyName = decodedToken.ext_attr.family_name || null;
}
if (response.statusCode === 401) {
debugTrace('requestToken: Call to /oauth/token was not successful (grant_type: user_token). Bearer token invalid, requesting client does not have grant_type=user_token or no scopes were granted.');
return cb(new Error('Call to /oauth/token was not successful (grant_type: user_token). Bearer token invalid, requesting client does not have grant_type=user_token or no scopes were granted.'), null);
userInfo.givenName = givenName || decodedToken.given_name || '';
userInfo.familyName = familyName || decodedToken.family_name || '';
userInfo.email = decodedToken.email || '';
userInfo.logonName = decodedToken.user_name || '';
debugTrace('\nObtained logon name: ' + this.getLogonName());
debugTrace('Obtained given name: ' + this.getGivenName());
debugTrace('Obtained family name: ' + this.getFamilyName());
debugTrace('Obtained email: ' + this.getEmail());
if (decodedToken.ext_cxt) {
userAttributes = decodedToken.ext_cxt['xs.user.attributes'] || null;
samlToken = decodedToken.ext_cxt['hdb.nameduser.saml'] || null;
} else {
userAttributes = decodedToken['xs.user.attributes'];
samlToken = decodedToken['hdb.nameduser.saml'] || null;
}
if (response.statusCode !== 200) {
debugTrace('requestToken: Call to /oauth/token was not successful (grant_type: user_token). HTTP status code: ' + response.statusCode);
return cb(new Error('Call to /oauth/token was not successful (grant_type: user_token). HTTP status code: ' + response.statusCode), null);
if(userAttributes) {
debugTrace('\nObtained attributes: ' + JSON.stringify(userAttributes, null, 4));
} else {
debugTrace('\nObtained attributes: no XS user attributes in JWT token available.');
}
var json = null;
try {
json = JSON.parse(body);
} catch (e) {
return cb(e, null);
}
// refresh token flow
options = {
url : urlWithCorrectSubdomain + '/oauth/token?grant_type=refresh_token&refresh_token=' + json.refresh_token,
headers: { Accept: 'application/json' },
auth: {
user: serviceCredentials.clientid,
pass: serviceCredentials.clientsecret
},
timeout: 2000
};
request.post(
options,
function(error, response, body) {
if (error) {
if (error.code === 'ETIMEDOUT' && error.connect === true) {
debugError('requestToken: HTTP connection timeout.');
}
debugError(error.message);
debugError(error.stack);
return cb(error, null);
}
if (response.statusCode === 401) {
debugTrace('requestToken: Call to /oauth/token was not successful (grant_type: refresh_token). Client credentials invalid.');
return cb(new Error('Call to /oauth/token was not successful (grant_type: refresh_token). Client credentials invalid.'), null);
}
if (response.statusCode !== 200) {
debugTrace('requestToken: Call to /oauth/token was not successful (grant_type: refresh_token). HTTP status code: ' + response.statusCode);
return cb(new Error('Call to /oauth/token was not successful (grant_type: refresh_token). HTTP status code ' + response.statusCode), null);
}
var json = null;
try {
json = JSON.parse(body);
} catch (e) {
return cb(e, null);
}
return cb(null, json.access_token)
}
);
}
);
}
function requestClientCredentialsToken(securityContext, serviceCredentials, additionalAttributes, cb) {
// input validation
if (!serviceCredentials) {
var error = new Error('Parameter serviceCredentials is missing but mandatory.');
return cb(error, null);
}
if (!serviceCredentials.clientid || !serviceCredentials.clientsecret) {
var error = new Error('Invalid service credentials: Missing clientid/clientsecret.');
return cb(error, null);
}
if (!serviceCredentials.url) {
var error = new Error('Invalid service credentials: Missing url.');
return cb(error, null);
}
// adapt subdomain in service url, if necessary
var urlWithCorrectSubdomain = serviceCredentials.url;
var tokenSubdomain = securityContext.getSubdomain();
var tokenRequestSubdomain = null;
var uaaUrl = url.parse(serviceCredentials.url);
if (uaaUrl.hostname.indexOf('.') === -1) {
tokenRequestSubdomain = null;
} else {
tokenRequestSubdomain = uaaUrl.hostname.substring(0, uaaUrl.hostname.indexOf('.'));
}
if (tokenSubdomain !== null && tokenRequestSubdomain != null && tokenSubdomain !== tokenRequestSubdomain) {
urlWithCorrectSubdomain = uaaUrl.protocol + "//" + tokenSubdomain + uaaUrl.host.substring(uaaUrl.host.indexOf('.'), uaaUrl.host.size);
}
// client credentials flow
var options = {
url : urlWithCorrectSubdomain + '/oauth/token?grant_type=client_credentials&response_type=token',
headers: { 'Accept' : 'application/json', 'Content-Type' : 'application/x-www-form-urlencoded' },
auth: {
user: serviceCredentials.clientid,
pass: serviceCredentials.clientsecret
},
timeout: 2000
};
if (additionalAttributes !== null) {
var authorities = { "az_attr" : additionalAttributes };
options.url = options.url + "&authorities=" + encodeURIComponent(JSON.stringify(authorities));
}
request.post(
options,
function(error, response, body) {
if (error) {
if (error.code === 'ETIMEDOUT' && error.connect === true) {
debugError('requestToken: HTTP connection timeout.');
}
debugError(error.message);
debugError(error.stack);
return cb(error, null);
}
if (response.statusCode === 401) {
debugTrace(body);
debugTrace('requestToken: Call to /oauth/token was not successful (grant_type: client_credentials). Client credentials invalid.');
return cb(new Error('Call to /oauth/token was not successful (grant_type: client_credentials). Client credentials invalid.'), null);
}
if (response.statusCode !== 200) {
debugTrace('requestToken: Call to /oauth/token was not successful (grant_type: client_credentials). HTTP status code: ' + response.statusCode);
return cb(new Error('Call to /oauth/token was not successful (grant_type: client_credentials). HTTP status code ' + response.statusCode), null);
}
var json = null;
try {
json = JSON.parse(body);
} catch (e) {
return cb(e, null);
}
return cb(null, json.access_token);
additionalAuthAttributes = decodedToken.az_attr || null;
if(additionalAuthAttributes) {
debugTrace('\nObtained additional authentication attributes: ' + JSON.stringify(additionalAuthAttributes, null, 4));
} else {
debugTrace('\nObtained attributes: no additional authentication attributes in JWT token available.');
}
);
};
function forbidClientCredentialsToken(functionName, grantType) {
if (grantType === constants.GRANTTYPE_CLIENTCREDENTIAL) {
var errorString = '\nCall to ' + functionName + ' not allowed with a token of grant type ' + constants.GRANTTYPE_CLIENTCREDENTIAL + '.';
debugTrace(errorString);
throw new Error(errorString);
if(decodedToken.ext_attr) {
serviceinstanceid = decodedToken.ext_attr.serviceinstanceid || null;
subdomain = decodedToken.ext_attr.zdn || null;
}
debugTrace('\nObtained subdomain: ' + this.getSubdomain());
debugTrace('Obtained serviceinstanceid: ' + this.getCloneServiceInstanceId());
debugTrace('Obtained origin: ' + this.getOrigin());
debugTrace('Obtained scopes: ' + JSON.stringify(scopes, null, 4));
}
};
this.verifyToken = function (encodedToken, cb) {
var verificationKey = new VerificationKey(config);
var jwtValidator = new JwtTokenValidator(verificationKey, config);
//Now validate the tokens
jwtValidator.validateToken(encodedToken, function(err, decodedToken) {
if(err) {
return cb(err);
}
isForeignMode = jwtValidator.isForeignMode();
//Token is now validated. So just fill local variables
fillContext.call(this, encodedToken, decodedToken);
cb(null, this);
}.bind(this));
};
//call constructor
ctor();
};

@@ -1,1 +0,1 @@

{"bundleDependencies":false,"dependencies":{"@sap/node-jwt":"^1.6.5","debug":"4.1.1","lru-cache":"5.1.1","request":"2.88.0","requestretry":"4.0.0","valid-url":"1.0.9"},"deprecated":false,"description":"XS Advanced Container Security API for node.js","devDependencies":{"@sap/xsenv":"^2.2.0","istanbul":"^0.4.5","jwt-decode":"^2.2.0","mocha":"^5.1.0","should":"^13.2.1"},"keywords":["xs"],"main":"./lib","name":"@sap/xssec","repository":{"type":"git","url":"ssh://git@github.wdf.sap.corp/xs2/node-xs2sec.git"},"scripts":{"prepareRelease":"npm prune --production","test":"make test"},"version":"2.2.4","license":"SEE LICENSE IN developer-license-3.1.txt"}
{"bundleDependencies":false,"dependencies":{"debug":"4.1.1","jsonwebtoken":"^8.5.1","lru-cache":"5.1.1","request":"2.88.0","requestretry":"4.0.0","valid-url":"1.0.9"},"deprecated":false,"description":"XS Advanced Container Security API for node.js","devDependencies":{"@sap/xsenv":"^2.2.0","istanbul":"^0.4.5","jwt-decode":"^2.2.0","mocha":"^5.1.0","should":"^13.2.1"},"keywords":["xs"],"main":"./lib","name":"@sap/xssec","repository":{"type":"git","url":"ssh://git@github.wdf.sap.corp/xs2/node-xs2sec.git"},"scripts":{"prepareRelease":"npm prune --production","test":"make test"},"version":"3.0.0","license":"SEE LICENSE IN developer-license-3.1.txt"}

@@ -247,10 +247,2 @@ @sap/xssec: XS Advanced Container Security API for node.js

### getToken (obsolete, use getHdbToken or getAppToken)
Parameters:
* `namespace` ... Tokens can eventually be used in different contexts, e.g. to access the HANA database, to access another XS2-based service such as the Job Scheduler, or even to access other applications/containers. To differentiate between these use cases, the `namespace` is used. In `lib/constants.js` we define supported namespaces (e.g. `SYSTEM`).
* `name` ... The name is used to differentiate between tokens in a given namespace, e.g. `HDB` for HANA database or `JOBSCHEDULER` for the job scheduler. These names are also defined in the file `lib/constants.js`.
* returns a token that can be used e.g. for contacting the HANA database. If the token, that the security context has been instantiated with, is a foreign token (meaning that the OAuth client contained in the token and the OAuth client of the current application do not match), `null` is returned instead of a token.
### getAppToken

@@ -273,12 +265,2 @@

### requestTokenForClient (obsolete, use requestToken instead)
Requests a token with `grant_type=user_token` from another client. Prerequisite is that the requesting client has `grant_type=user_token` and that the current user token includes the scope `uaa.user`.
Parameters:
* `serviceCredentials` ... the credentials of the service as JSON object. The attributes `clientid`, `clientsecret` and `url` (UAA) are mandatory.
* `scopes` ... comma-separated list of requested scopes for the token, e.g. `app.scope1,app.scope2`. If null, all scopes are granted. Note that $XSAPPNAME is not supported as part of the scope names.
* `cb(error, token)` ... callback function
### hasAttributes

@@ -318,6 +300,2 @@

### getIdentityZone (obsolete, use getSubaccountId instead)
* returns the identity zone that the access token has been issued for.
### getSubaccountId

@@ -338,1 +316,8 @@

* returns the grant type of the JWT token, e.g. `authorization_code`, `password`, `client_credentials` or `urn:ietf:params:oauth:grant-type:saml2-bearer`.
## Latest published Version
Use this command to check for the latest version that is published to the NPM repository:
```
npm view --registry https://npm.sap.com @sap/xssec versions
```

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc