Socket
Socket
Sign inDemoInstall

@sap/xssec

Package Overview
Dependencies
7
Maintainers
1
Versions
70
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 3.1.2 to 3.2.0

doc/IAS.md

5

CHANGELOG.md
# Change Log
All notable changes to this project will be documented in this file.
## 3.1.2 - 2020-03-01
- Feature: Support for IAS to XSUAA token exchange ([more details](doc/IAStoXSUAA.md))
- Feature: Support for ZoneID enabled token flows ([more details](doc/TokenFlows.md))
## 3.1.1 - 2020-02-11

@@ -5,0 +10,0 @@ - Bugfix: Tokenexchange with additional attributes may result in a wrong formatted url

4

lib/index.js

@@ -12,2 +12,4 @@ 'use strict';

exports.config = require('./xssec.config');
exports.TokenInfo = require('./tokeninfo');
exports.TokenInfo = require('./tokeninfo');
exports.errors = require('./errors');
'use strict';
const constants = require('./constants');
const LRU = require('lru-cache');
const nodeRSA = require('node-rsa');
const validUrl = require('valid-url');

@@ -12,2 +12,3 @@

const constants = require('./constants');
const requests = require('./requests');

@@ -80,2 +81,87 @@

function createPublicKeyFromJWKS(json) {
if (json.kty !== "RSA") {
throw new Error("KTY '" + json.kty + "' not supported");
}
const modulus = Buffer.from(json.n, 'base64');
const exponent = Buffer.from(json.e, 'base64');
const pubKey = new nodeRSA().importKey({ n: modulus, e: exponent }, 'components-public');
return pubKey.exportKey('pkcs8-public-pem');
}
KeyCache.prototype.getWellKnownFromOIDC = function(serviceUrl, cb) {
var cacheKey = serviceUrl + ".well_known.";
var tmpResult = this.lruCache.get(cacheKey);
if(tmpResult) {
cb(null, tmpResult);
} else {
var self = this;
requests.requestOpenIDConfiguration(serviceUrl, function(err, result) {
if(err) {
return cb(err);
}
self.addKey(cacheKey, result);
cb(null, result);
});
}
}
KeyCache.prototype.getKeyOIDC = function(serviceUrl, keyId, cb) {
var self = this;
this.getWellKnownFromOIDC(serviceUrl, function(err, result) {
if(err) {
return cb(err);
}
let tokenUrl = result.jwks_uri;
let tmpResult = self.lruCache.get(tokenUrl + keyId);
if(tmpResult) {
//found in Cache
return cb(null, tmpResult);
}
requests.fetchOIDCKey(tokenUrl, function(err, result) {
if(err) {
return cb(err);
}
//add all Keys to cache
for(var i=0;i<result.keys.length;++i) {
var key = result.keys[i];
try {
self.addKey(tokenUrl + key.kid, createPublicKeyFromJWKS(key));
} catch(e) {
debugError(e);
}
}
var cacheKey = tokenUrl + keyId;
var tmpResult = self.lruCache.get(cacheKey);
if(tmpResult) {
cb(null, tmpResult);
} else {
cb(new Error("Verfication Key not found"));
}
});
});
requests.requestOpenIDConfiguration(serviceUrl, function(err, result) {
if(err) {
return cb(err);
}
var tokenUrl = result.jwks_uri;
});
}
KeyCache.prototype.getKey = function getKey(tokenKeyUrl, keyId, zid, cb) {

@@ -137,72 +223,3 @@ var self = this;

});
request
.get(
options,
function(err, response, body) {
if (response) {
debugTrace('Finished after number of requests attempts: ' + response.attempts);
}
if (err) {
if (err.code === 'ETIMEDOUT' && err.connect === true) {
debugError('getKey: HTTP connection timeout.');
}
debugError('An error occurred when reading the token keys from '
+ options.url
+ ': '
+ err.message
+ err.stack);
var error = new Error(
'An error occurred when reading the token keys from '
+ options.url + ': '
+ err.message + err.stack);
return process.nextTick(function() {
cb(error, null);
});
}
if (response.statusCode !== 200) {
debugTrace('Call was not successful. Error Code: '
+ response.statusCode);
var error = new Error(
'Call was not successful. Error Code: '
+ response.statusCode);
return process.nextTick(function() {
cb(error, null);
});
}
var json = null;
try {
json = JSON.parse(body);
for (var i = 0; i < json.keys.length; i++) {
// Note: The following code removes line
// breaks before adding the key to the cache
self.addKey(tokenKeyUrl + json.keys[i].kid,
json.keys[i].value.replace(
/(\r\n|\n|\r)/gm, ''));
}
} catch (e) {
debugError('Error parsing response from UAA: '
+ e)
var error = new Error(
'Error parsing response from UAA: ' + e)
return process.nextTick(function() {
cb(error, null);
});
}
var tmpResult = self.lruCache.get(cacheKey);
if (tmpResult !== undefined) {
debugTrace('Key "' + cacheKey + '" found in cache. Returning key "' + tmpResult + '".');
return process.nextTick(function() {
cb(null, tmpResult);
});
} else {
var error = new Error(
'Obtained token keys from UAA, but key with requested keyID "' +
cacheKey + '" still not found in cache.');
return process.nextTick(function() {
cb(error, null);
});
}
});
}
};

@@ -8,4 +8,7 @@ 'use strict';

const errors = require('./errors');
// use environment variable DEBUG with value 'xssec:*' for trace/error messages
const debug = require('debug');
const { exit } = require('process');
const debugTrace = debug('xssec:requests');

@@ -19,30 +22,39 @@ const debugError = debug('xssec:requests');

function _requestToXSUAA(fnc, options, retryOptions, cb) {
function getTokenResponseHandler(cb) {
return function(error, response, body) {
if (error) {
if (error.code === 'ETIMEDOUT' && error.connect === true) {
debugError(fnc + ': HTTP connection timeout.');
}
error.statusCode = -1;
error.description = error.message;
debugError(error.message);
debugError(error.stack);
return cb(error);
}
if (response.statusCode !== 200) {
return cb(new errors.ServerRequestError(response.statusCode + " - " + body, response.statusCode, body));
}
var json = null;
try {
json = JSON.parse(body);
} catch (e) {
e.statusCode = -1;
e.description = "Could not parse JSON";
e.content = body;
return cb(e, null);
}
return cb(null, json.id_token || json.access_token || json, json);
}
}
function _requestToNetwork(fnc, options, cb) {
debugTrace(fnc + '::HTTP Call with %O', options);
request.post(
request(
options,
function (error, response, body) {
if (error) {
if (error.code === 'ETIMEDOUT' && error.connect === true) {
debugError( fnc + ': HTTP connection timeout.');
}
debugError(error.message);
debugError(error.stack);
return cb(error, null);
}
if (response.statusCode !== 200) {
return cb(new Error(response.statusCode + " - " + body));
}
var json = null;
try {
json = JSON.parse(body);
} catch (e) {
return cb(e, null);
}
return cb(null, json.access_token, json)
}
cb
);
};
function validateParameters(serviceCredentials, cb) {

@@ -108,2 +120,3 @@ // input validation

var options = {
method: 'POST',
url: url + '/oauth/token',

@@ -125,2 +138,32 @@ headers: DefaultHeaders(zoneId),

module.exports.requestOpenIDConfiguration = function (serviceCredentialsUrl, cb) {
var options = {
method: 'GET',
followRedirect: false,
timeout: 2000,
url: serviceCredentialsUrl + '/.well-known/openid-configuration',
headers: {
'Accept': 'application/json',
'User-Agent': constants.USER_AGENT
}
};
return _requestToNetwork(".well-known", options, getTokenResponseHandler(cb));
}
module.exports.fetchOIDCKey = function (serviceCredentialsUrl, cb) {
var options = {
method: 'GET',
url: serviceCredentialsUrl,
followRedirect: false,
timeout: 2000,
headers: {
'Accept': 'application/json',
'User-Agent': constants.USER_AGENT
}
};
return _requestToNetwork(".oidc-jkws", options, getTokenResponseHandler(cb));
}
module.exports.requestUserToken = function (appToken, serviceCredentials, additionalAttributes, scopes, subdomain, zoneId, cb) {

@@ -155,3 +198,3 @@ //make it backward-compatible (where zoneId is not provided at all)

return _requestToXSUAA("requestUserToken", options, false, cb);
return _requestToNetwork("requestUserToken", options, getTokenResponseHandler(cb));
}

@@ -184,3 +227,3 @@

return _requestToXSUAA("requestClientCredentialsToken", options, false, cb);
return _requestToNetwork("requestClientCredentialsToken", options, getTokenResponseHandler(cb));
};

@@ -222,2 +265,18 @@

});
}
module.exports.__patchNetwork = new function() {
var oldrequestToXSUAA = _requestToNetwork;
this.patch = function(fnc) {
if(typeof fnc === 'function') {
debugTrace("patch XSUAA communication to another function");
_requestToNetwork = fnc;
} else {
if(_requestToNetwork !== oldrequestToXSUAA) {
debugTrace("patch XSUAA communication to original function");
_requestToNetwork = oldrequestToXSUAA;
}
}
}
}

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

function JWTStrategy(options) {
function JWTStrategy(options, forceType) {
this.options = options;
this.name = 'JWT';
this._forceType = forceType;
}

@@ -61,4 +62,4 @@

try {
var self = this;
xssec.createSecurityContext(token, this.options, function (err, ctx, tokenInfo) {
function callback(err, ctx, tokenInfo) {
req.tokenInfo = tokenInfo;

@@ -94,3 +95,9 @@

self.success(user, ctx);
});
};
var self = this;
var paramA = this._forceType ? this._forceType : callback;
var paramB = this._forceType ? callback : undefined;
xssec.createSecurityContext(token, this.options, paramA, paramB);
}

@@ -97,0 +104,0 @@ catch (err) {

@@ -89,2 +89,10 @@ 'use strict';

this.getUserId = function() {
if(this.isTokenIssuedByXSUAA()) {
return payload.user_uuid || payload.sub;
} else {
return payload.user_uuid;
}
}
this.getZoneId = function() {

@@ -91,0 +99,0 @@ if(this.isTokenIssuedByXSUAA()) {

@@ -163,3 +163,49 @@ 'use strict';

function JwtTokenValidator(verificationKey, configArray, serviceCredentials) {
function JwtTokenValidatorIAS(verificationKey, configArray, serviceCredentials) {
this.isForeignMode = function () {
return false;
}
this.validateToken = function (accessToken, cb) {
function returnError(code, errorString) {
debugError('\n' + errorString);
var error = new Error(errorString);
error.statuscode = code;
return cb(error);
}
var token = new TokenInfo(accessToken);
return token.verify(verificationKey.getCallback(token), function(err, token) {
if (err) {
debugError('\n' + err.message);
err.statuscode = 401;
return cb(err);
}
//Issuer validation
var iss = token.getPayload().iss;
if(iss !== serviceCredentials.url) {
returnError(401, "Issuer validation failed (iss=" + iss + ") url=" + serviceCredentials.url);
}
//Audience validation
let audienceValidator = new JwtAudienceValidator(serviceCredentials.clientid);
for (var i = 1; i < configArray.length; ++i) {
if (configArray[i] && configArray[i].clientid) {
audienceValidator.configureTrustedClientId(configArray[i].clientid);
}
}
let valid_result = audienceValidator.validateToken(token.getAudiencesArray());
if (!valid_result.isValid()) {
return returnError(401, valid_result.getErrorDescription());
}
cb(null, token);
});
}
}
function JwtTokenValidatorXSUAA(verificationKey, configArray, serviceCredentials) {
var foreignMode = false;

@@ -183,11 +229,11 @@

return tokeninfo.prepareToken(accessToken,
function (errorString, token) {
if (errorString) {
return returnError(401, errorString);
function (err, token) {
if (err) {
debugError('\n' + err.message);
err.statuscode = 401;
return cb(err);
}
return token.verify(verificationKey.getCallback(token),
function (err, token) {
var decodedToken = token.getPayload();
function (err, token) {
if (err) {

@@ -200,2 +246,4 @@ debugError(err.statuscode);

var decodedToken = token.getPayload();
if (!token.getClientId()) {

@@ -241,5 +289,13 @@ return returnError(400, 'Client Id not contained in access token. Giving up!');

function createJWTTokenValidator(verificationKey, configArray, serviceCredentials) {
if(verificationKey.getType() === "XSUAA") {
return new JwtTokenValidatorXSUAA(verificationKey, configArray, serviceCredentials);
} else {
return new JwtTokenValidatorIAS(verificationKey, configArray, serviceCredentials);
}
}
module.exports = {
JwtAudienceValidator: JwtAudienceValidator,
JwtTokenValidator: JwtTokenValidator
createJwtTokenValidator: createJWTTokenValidator
};

@@ -13,9 +13,17 @@ 'use strict';

function VerificationKey(config) {
function VerificationKey(config, type) {
var tokenInfo = null;
this.getCallback = function(token) {
tokenInfo = token;
return this.loadKey.bind(this);
if(type === 'XSUAA') {
return this.loadKeyXSUAA.bind(this);
} else {
return this.loadKeyOIDC.bind(this);
}
}
this.getType = function() {
return type;
}
function cleanUp(pem) {

@@ -47,3 +55,29 @@ if(!pem) {

this.loadKey = function(accessToken, cb) {
function getKeyCache() {
if(!keyCache) {
keyCache = new keycache.KeyCache(config.keyCache.cacheSize, config.keyCache.expirationTime);
}
return keyCache;
}
this.loadKeyOIDC = function(accessToken, cb) {
if (!accessToken.kid) {
return cb(new Error("No key identifier found in token"));
}
let keyCache = getKeyCache();
//try to get a key from KeyCache
keyCache.getKeyOIDC(config.url, accessToken.kid, function(err, key) {
if (err) {
debugTrace('\n' + err);
return cb(err);
} else {
return cb(null, key);
}
});
}
this.loadKeyXSUAA = function(accessToken, cb) {
var zid = tokenInfo.getPayload().zid;

@@ -61,10 +95,8 @@ if (!accessToken.kid || accessToken.kid == 'legacy-token-key' || !accessToken.jku) {

if(!keyCache) {
keyCache = new keycache.KeyCache(config.keyCache.cacheSize, config.keyCache.expirationTime);
}
let keyCache = getKeyCache();
//try to get a key from KeyCache
//try to get a key from KeyCache
keyCache.getKey(accessToken.jku, accessToken.kid, zid, function(err, key) {
if (err) {
debugTrace('\n', err);
debugTrace('\n' + err);
return cb(null, cleanUp(config.verificationkey));

@@ -71,0 +103,0 @@ } else {

'use strict';
const constants = require('./constants');
const url = require('url');
const requests = require('./requests');
const xsuaa = require('./ctx/xsuaa');
const ias = require('./ctx/ias');
// use environment variable DEBUG with value 'xssec:*' for trace/error messages
var debug = require('debug');
var debugTrace = debug('xssec:securitycontext');
var debugError = debug('xssec:securitycontext');
const debug = require('debug');
const debugTrace = debug('xssec:securitycontext');
const debugError = debug('xssec:securitycontext');
const JwtTokenValidator = require('./validator').JwtTokenValidator;
const VerificationKey = require('./verificationkey');
const { isArray } = require('util');
const { config } = require('process');
debugError.log = console.error.bind(console);

@@ -27,419 +22,147 @@ debugTrace.log = console.log.bind(console);

//For Backward compatibilty
exports.createSecurityContext = function (token, config, cb) {
try {
var securityContext = new SecurityContext(config);
securityContext.verifyToken(token, cb);
} catch (e) {
cb(e);
function validateConfig(config) {
// 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;
}, 2));
// validate config input
if (!config) {
return throw500('Invalid config (missing).');
}
}
function SecurityContext(configParam) {
//make sure the parameter is an array
var configArr = Array.isArray(configParam) ? configParam : [configParam];
//our main config is always the config at position 0
var config = configArr[0];
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.');
}
var userInfo = {
logonName: '',
givenName: '',
familyName: '',
email: ''
};
var token;
var xsappname;
var scopes;
var samlToken;
var clientId;
var subaccountid;
var zid;
var subdomain = null;
var origin = null;
var userAttributes;
var additionalAuthAttributes;
var serviceinstanceid = null;
var grantType;
var expirationDate;
var tokenInfo = null;
var isForeignMode = false;
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.';
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.');
if (!config.keyCache) {
config.keyCache = {
expirationTime: constants.KEYCACHE_DEFAULT_CACHE_ENTRY_EXPIRATION_TIME_IN_MINUTES,
cacheSize: constants.KEYCACHE_DEFAULT_CACHE_SIZE
};
debugTrace("Using KeyCache with default values %o", config.keyCache);
} else {
if (config.keyCache.expirationTime) {
//if it's provided it has to be a number bigger than zero
if (typeof config.keyCache.expirationTime !== 'number' || config.keyCache.expirationTime < constants.KEYCACHE_DEFAULT_CACHE_ENTRY_EXPIRATION_TIME_IN_MINUTES) {
debugError("keyCache.expirationTime has to be a Number with the value of at least " + constants.KEYCACHE_DEFAULT_CACHE_ENTRY_EXPIRATION_TIME_IN_MINUTES + ". taking default value.");
config.keyCache.expirationTime = constants.KEYCACHE_DEFAULT_CACHE_ENTRY_EXPIRATION_TIME_IN_MINUTES;
}
} else {
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'
+ '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.';
return throw500(errorString);
}
}
config.keyCache.expirationTime = constants.KEYCACHE_DEFAULT_CACHE_ENTRY_EXPIRATION_TIME_IN_MINUTES;
}
}
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));
// validate config input
if (!config) {
return throw500('Invalid config (missing).');
}
validateXSAPPNAME();
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.');
}
if(!config.keyCache) {
config.keyCache = {
expirationTime: constants.KEYCACHE_DEFAULT_CACHE_ENTRY_EXPIRATION_TIME_IN_MINUTES,
cacheSize: constants.KEYCACHE_DEFAULT_CACHE_SIZE
};
debugTrace("Using KeyCache with default values %o", config.keyCache);
} else {
if(config.keyCache.expirationTime) {
//if it's provided it has to be a number bigger than zero
if(typeof config.keyCache.expirationTime !== 'number' || config.keyCache.expirationTime < constants.KEYCACHE_DEFAULT_CACHE_ENTRY_EXPIRATION_TIME_IN_MINUTES) {
debugError("keyCache.expirationTime has to be a Number with the value of at least " + constants.KEYCACHE_DEFAULT_CACHE_ENTRY_EXPIRATION_TIME_IN_MINUTES + ". taking default value.");
config.keyCache.expirationTime = constants.KEYCACHE_DEFAULT_CACHE_ENTRY_EXPIRATION_TIME_IN_MINUTES;
}
} else {
config.keyCache.expirationTime = constants.KEYCACHE_DEFAULT_CACHE_ENTRY_EXPIRATION_TIME_IN_MINUTES;
}
if(config.keyCache.cacheSize) {
//if it's provided it has to be a number bigger than zero
if(typeof config.keyCache.cacheSize !== 'number' || config.keyCache.cacheSize < constants.KEYCACHE_DEFAULT_CACHE_SIZE) {
debugError("keyCache.cacheSize has to be a Number of at least " + constants.KEYCACHE_DEFAULT_CACHE_SIZE + ". taking default value");
config.keyCache.cacheSize = constants.KEYCACHE_DEFAULT_CACHE_SIZE;
}
} else {
if (config.keyCache.cacheSize) {
//if it's provided it has to be a number bigger than zero
if (typeof config.keyCache.cacheSize !== 'number' || config.keyCache.cacheSize < constants.KEYCACHE_DEFAULT_CACHE_SIZE) {
debugError("keyCache.cacheSize has to be a Number of at least " + constants.KEYCACHE_DEFAULT_CACHE_SIZE + ". taking default value");
config.keyCache.cacheSize = constants.KEYCACHE_DEFAULT_CACHE_SIZE;
}
debugTrace("Using KeyCache with custom values %o", config.keyCache);
} else {
config.keyCache.cacheSize = constants.KEYCACHE_DEFAULT_CACHE_SIZE;
}
}
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;
debugTrace("Using KeyCache with custom values %o", config.keyCache);
}
}
this.getSubaccountId = function () {
return subaccountid;
};
function validateXSUAAConfig(config) {
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.';
return throw500(errorString);
}
this.getZoneId = function () {
return zid;
};
this.getSubdomain = function () {
return subdomain;
};
this.getClientId = function () {
return clientId;
};
this.getExpirationDate = function () {
return expirationDate;
};
this.getOrigin = function () {
return origin;
};
this.getLogonName = function () {
return ifNotClientCredentialsToken('SecurityContext.getLogonName', userInfo.logonName);
};
this.getGivenName = function () {
return ifNotClientCredentialsToken('SecurityContext.getGivenName', userInfo.givenName);
};
this.getFamilyName = function () {
return ifNotClientCredentialsToken('SecurityContext.getFamilyName', userInfo.familyName);
};
this.getEmail = function () {
return ifNotClientCredentialsToken('SecurityContext.getEmail', userInfo.email);
};
this.getUserName = function () {
if (grantType === constants.GRANTTYPE_CLIENTCREDENTIAL) {
return `client/${clientId}`;
} else {
return this.getUniquePrincipalName(origin, userInfo.logonName);
debugTrace('\nXSAPPNAME defined in manifest.yml (legacy).\n'
+ 'You should switch to defining xsappname in xs-security.json.');
return process.env.XSAPPNAME;
} else {
if (!process.env.XSAPPNAME) {
return config.xsappname;
}
};
this.getUniquePrincipalName = function (origin, logonName) {
if(!ifNotClientCredentialsToken('SecurityContext.getUniquePrincipalName', true)) {
return null;
if (process.env.XSAPPNAME == config.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.');
return process.env.XSAPPNAME;
}
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}`;
};
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 getHdbToken function does not return a valid token.\n');
return null;
}
return samlToken ? samlToken : this.getAppToken();
};
this.getAppToken = function () {
return token;
};
this.getTokenInfo = function() {
return tokenInfo;
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.';
return throw500(errorString);
}
}
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'
+ '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 getAttribute function does not return any attributes.\n');
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];
};
function validateIASConfig(config) {
//TODO: clarify do we need to check something here?
}
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];
};
this.getCloneServiceInstanceId = function () {
return serviceinstanceid;
};
this.isInForeignMode = function () {
return isForeignMode;
};
this.hasAttributes = function () {
return ifNotClientCredentialsToken('SecurityContext.hasAttributes', userAttributes ? true : false);
};
this.checkLocalScope = function (scope) {
if (!scope || !scopes) {
function takeXSUAAConfiguration(config, forceType) {
if(forceType === exports.XSUAA) {
debugTrace("Creating a XSUAA securityContext because of forceType");
return true;
} if(forceType === exports.IAS) {
debugTrace("Creating a IAS securityContext because of forceType");
return false;
} else if(!forceType) {
if(config.xsappname ? true : (process.env.XSAPPNAME ? true : false)) {
debugTrace("Creating a XSUAA securityContext because of xsappname");
return true;
} else {
debugTrace("Creating a IAS securityContext because of missing xsappname");
return false;
}
var scopeName = xsappname + '.' + scope;
return scopes.indexOf(scopeName) !== -1;
};
}
this.getGrantType = function () {
return grantType;
};
throw500("Unknown SecurityContextType (" + forceType + ")");
}
this.checkScope = function (scope) {
if (!scope || !scopes) {
return false;
}
exports.XSUAA = "XSUAA";
exports.IAS = "IAS";
if (scope.substring(0, constants.XSAPPNAMEPREFIX.length) === constants.XSAPPNAMEPREFIX) {
scope = scope.replace(constants.XSAPPNAMEPREFIX, xsappname + '.');
}
return scopes.indexOf(scope) !== -1;
};
//For Backward compatibilty
exports.createSecurityContext = function (token, configParam, forceType, cb) {
if(typeof forceType === 'function') {
cb = forceType;
forceType = null;
}
this.requestToken = function (serviceCredentials, type, additionalAttributes, cb) {
if (type === constants.TYPE_USER_TOKEN) {
return requests.requestUserToken(this.getAppToken(), serviceCredentials, additionalAttributes, null, this.getSubdomain(), cb);
} else if (type === constants.TYPE_CLIENT_CREDENTIALS_TOKEN) {
return requests.requestClientCredentialsToken(this.getSubdomain(), serviceCredentials, additionalAttributes, cb);
} else {
return cb(new Error('Invalid grant type.'));
}
};
function cleanUpUserAttributes(attr) {
if(!attr) {
return null;
}
var len = 0;
for(var n in attr) {
++len;
}
return len == 0 ? null : attr;
if(!cb || typeof cb !== 'function') {
throw500("The callback parameter should be a function");
}
function fillContext(encodedToken, info) {
tokenInfo = info;
var decodedToken = tokenInfo.getPayload();
debugTrace('\nApplication received a token of grant type "' + decodedToken.grant_type + '".');
try {
//make sure the parameter is an array
var configArr = Array.isArray(configParam) ? configParam : [configParam];
token = encodedToken;
scopes = decodedToken.scope || [];
zid = decodedToken.zid;
subaccountid = decodedToken["ext_attr"] ? decodedToken["ext_attr"].subaccountid : zid;
if(!subaccountid) {
subaccountid = zid;
}
//our main config is always the config at position 0
var config = configArr[0];
validateConfig(config);
clientId = tokenInfo.getClientId();
expirationDate = new Date(decodedToken.exp * 1000);
grantType = decodedToken.grant_type;
let securityContext;
if(takeXSUAAConfiguration(config, forceType)) {
//will return the xsappname to use (env or config)
config.xsappname = validateXSUAAConfig(config);
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;
}
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;
}
userAttributes = cleanUpUserAttributes(userAttributes);
if(userAttributes) {
debugTrace('\nObtained attributes: ' + JSON.stringify(userAttributes, null, 4));
} else {
debugTrace('\nObtained attributes: no XS user attributes in JWT token available.');
}
}
additionalAuthAttributes = decodedToken.az_attr || null;
if(additionalAuthAttributes) {
debugTrace('\nObtained additional authentication attributes: ' + JSON.stringify(additionalAuthAttributes, null, 4));
//create a XSUAA security Context
securityContext = new xsuaa.SecurityContext(config, configArr);
} else {
debugTrace('\nObtained attributes: no additional authentication attributes in JWT token available.');
validateIASConfig(config);
//create a IAS security Context
securityContext = new ias.SecurityContext(config, configArr);
}
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));
securityContext.verifyToken(token, cb);
} catch (e) {
cb(e);
}
this.verifyToken = function (encodedToken, cb) {
var verificationKey = new VerificationKey(config);
var jwtValidator = new JwtTokenValidator(verificationKey, configArr, config);
//Now validate the tokens
jwtValidator.validateToken(encodedToken, function(err, tokenInfo) {
if(err) {
return cb(err, null, tokenInfo);
}
isForeignMode = jwtValidator.isForeignMode();
//Token is now validated. So just fill local variables
fillContext.call(this, encodedToken, tokenInfo);
cb(null, this, tokenInfo);
}.bind(this));
};
//call constructor
ctor();
};
}
{
"name": "@sap/xssec",
"version": "3.1.2",
"version": "3.2.0",
"description": "XS Advanced Container Security API for node.js",

@@ -13,2 +13,5 @@ "main": "./lib",

"lib",
"lib/ctx",
"lib/strategies",
"doc",
"package.json",

@@ -34,3 +37,4 @@ "README.md"

"jsonwebtoken": "^8.5.1",
"lru-cache": "5.1.1",
"lru-cache": "6.0.0",
"node-rsa": "^1.1.1",
"request": "^2.88.2",

@@ -37,0 +41,0 @@ "requestretry": "4.0.0",

SocketSocket SOC 2 Logo

Product

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

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc