Socket
Socket
Sign inDemoInstall

@sap/xssec

Package Overview
Dependencies
Maintainers
1
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 3.1.1 to 3.1.2

lib/tokenexchanger.js

5

lib/index.js

@@ -9,2 +9,5 @@ 'use strict';

exports.JWTStrategy = passportStrategy.JWTStrategy;
exports.requests = require('./requests');
exports.requests = require('./requests');
exports.config = require('./xssec.config');
exports.TokenInfo = require('./tokeninfo');

235

lib/requests.js

@@ -16,17 +16,50 @@ 'use strict';

module.exports.requestUserToken = function (appToken, serviceCredentials, additionalAttributes, scopes, subdomain, cb) {
const X_ZONE_ID_HEADER_NAME = "x-zid";
function _requestToXSUAA(fnc, options, retryOptions, cb) {
debugTrace(fnc + '::HTTP Call with %O', options);
request.post(
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)
}
);
};
function validateParameters(serviceCredentials, cb) {
// input validation
if (!serviceCredentials) {
var error = new Error('Parameter serviceCredentials is missing but mandatory.');
return cb(error, null);
return new Error('Parameter serviceCredentials is missing but mandatory.');
}
if (!serviceCredentials.clientid || !serviceCredentials.clientsecret) {
var error = new Error('Invalid service credentials: Missing clientid/clientsecret.');
return cb(error, null);
return new Error('Invalid service credentials: Missing clientid/clientsecret.');
}
if (!serviceCredentials.url) {
var error = new Error('Invalid service credentials: Missing url.');
return cb(error, null);
return new Error('Invalid service credentials: Missing url.');
}
if(!cb || typeof cb !== 'function') {
return new Error('No callback function provided.');
}
}
function buildSubdomain(serviceCredentials, subdomain) {
var urlWithCorrectSubdomain = serviceCredentials.url;

@@ -47,125 +80,103 @@ if (subdomain) {

return urlWithCorrectSubdomain;
}
function appendAdditonalAttribites(options, additionalAttributes) {
if (additionalAttributes !== null) {
var authorities = { "az_attr" : additionalAttributes };
options.form.authorities = authorities;
}
}
function DefaultHeaders(zoneId) {
var ret = {
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': constants.USER_AGENT
};
if(zoneId) {
ret[X_ZONE_ID_HEADER_NAME] = zoneId;
}
return ret;
}
function buildOptions(serviceCredentials, additionalAttributes, url, grantType, zoneId, timeout) {
// jwt bearer flow
var options = {
url: urlWithCorrectSubdomain + '/oauth/token',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': constants.USER_AGENT
},
url: url + '/oauth/token',
headers: DefaultHeaders(zoneId),
form: {
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
grant_type: grantType,
response_type: 'token',
client_id: serviceCredentials.clientid,
client_secret: serviceCredentials.clientsecret,
assertion: appToken
client_secret: serviceCredentials.clientsecret
},
timeout: 10*1000
timeout: timeout
};
appendAdditonalAttribites(options, additionalAttributes);
return options;
}
module.exports.requestUserToken = function (appToken, serviceCredentials, additionalAttributes, scopes, subdomain, zoneId, cb) {
//make it backward-compatible (where zoneId is not provided at all)
if (typeof zoneId === 'function') {
cb = zoneId;
zoneId = null;
}
var error = validateParameters(serviceCredentials, cb);
if(error) {
return cb(error, null);
}
var urlWithCorrectSubdomain = buildSubdomain(serviceCredentials, subdomain);
// jwt bearer flow
var options = buildOptions(serviceCredentials,
additionalAttributes,
urlWithCorrectSubdomain,
'urn:ietf:params:oauth:grant-type:jwt-bearer',
zoneId,
10*1000);
//add Assertion
options.form.assertion = appToken;
if (scopes !== null) {
options.form.scope = scopes;
}
if (additionalAttributes !== null) {
var authorities = { "az_attr" : additionalAttributes };
options.form.authorities = authorities;
}
debugTrace('requestUserToken::HTTP Call with %O', options);
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 !== 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)
}
);
return _requestToXSUAA("requestUserToken", options, false, cb);
}
module.exports.requestClientCredentialsToken = function (subdomain, serviceCredentials, additionalAttributes, cb) {
module.exports.requestClientCredentialsToken = function (subdomain, serviceCredentials, additionalAttributes, zoneId, cb) {
//make it backward-compatible (where zoneId is not provided at all)
if (typeof zoneId === 'function') {
cb = zoneId;
zoneId = null;
}
// input validation
if (!serviceCredentials) {
var error = new Error('Parameter serviceCredentials is missing but mandatory.');
var error = validateParameters(serviceCredentials, cb);
if(error) {
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 = subdomain;
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',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': constants.USER_AGENT
},
form: {
grant_type: 'client_credentials',
response_type: 'token',
client_id: serviceCredentials.clientid,
client_secret: serviceCredentials.clientsecret
},
timeout: 2 * 1000
};
if (additionalAttributes !== null) {
var authorities = { "az_attr": additionalAttributes };
options.url = options.url + "?authorities=" + encodeURIComponent(JSON.stringify(authorities));
}
debugTrace('requestClientCredentialsToken::HTTP Call with %O', options);
requestRetry.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);
}
if (response.statusCode !== 200) {
return cb(new Error(response.statusCode + " - " + body));
}
var json = null;
try {
json = JSON.parse(body);
} catch (e) {
return cb(e);
}
return cb(null, json.access_token, json);
}
);
var urlWithCorrectSubdomain = buildSubdomain(serviceCredentials, subdomain);
var options = buildOptions(serviceCredentials,
additionalAttributes,
urlWithCorrectSubdomain,
'client_credentials',
zoneId,
2*1000);
appendAdditonalAttribites(options, additionalAttributes);
return _requestToXSUAA("requestClientCredentialsToken", options, false, cb);
};

@@ -172,0 +183,0 @@

@@ -26,2 +26,5 @@ 'use strict';

}
} else {
errobj = new jwt.JsonWebTokenError("jwt undefined");
errobj.statuscode = 400;
}

@@ -87,2 +90,10 @@ }

this.getZoneId = function() {
if(this.isTokenIssuedByXSUAA()) {
return payload.zid;
} else {
return payload.zone_uuid;
}
}
this.getClientId = function() {

@@ -89,0 +100,0 @@ var azp = payload.azp;

'use strict';
const util = require('util');

@@ -10,4 +9,8 @@

const jwt = require('jsonwebtoken');
const tokenInfo = require('./tokeninfo')
const TokenInfo = require('./tokeninfo');
const TokenExchanger = require('./tokenexchanger');
const requests = require('./requests');
const DOT = ".";

@@ -65,3 +68,3 @@

this.isForeignMode = function() {
this.isForeignMode = function () {
return foreignMode;

@@ -71,3 +74,3 @@ }

function validateSameClientId(cidFromToken) {
if(!cidFromToken || !clientId) {
if (!cidFromToken || !clientId) {
return false;

@@ -110,3 +113,3 @@ }

if(ret === null) {
if (ret === null) {
foreignMode = true;

@@ -117,3 +120,3 @@ }

this.getListOfAudiencesFromToken = function(aud, scopes, cid) {
this.getListOfAudiencesFromToken = function (aud, scopes, cid) {
return extractAudiencesFromToken(aud || [], scopes || [], cid);

@@ -141,7 +144,7 @@ }

if (audiences.length == 0) {
for(var i=0;i < scopes.length;++i) {
for (var i = 0; i < scopes.length; ++i) {
var scope = scopes[i];
if (scope.indexOf(DOT) >-1) {
if (scope.indexOf(DOT) > -1) {
var aud = scope.substring(0, scope.indexOf(DOT)).trim();
if(aud && !audiences.includes(aud)) {
if (aud && !audiences.includes(aud)) {
audiences.push(aud);

@@ -152,7 +155,7 @@ }

}
if(cid && audiences.indexOf(cid) === -1) {
if (cid && audiences.indexOf(cid) === -1) {
audiences.push(cid);
}
return audiences;

@@ -167,6 +170,6 @@ }

function JwtTokenValidator(verificationKey, configArray) {
function JwtTokenValidator(verificationKey, configArray, serviceCredentials) {
var foreignMode = false;
this.isForeignMode = function() {
this.isForeignMode = function () {
return foreignMode;

@@ -184,51 +187,58 @@ }

var token = new tokenInfo(accessToken);
let tokeninfo = new TokenExchanger(serviceCredentials);
token.verify(verificationKey.getCallback(token),
function (err, token) {
var decodedToken = token.getPayload();
if (err) {
debugError(err.statuscode);
debugError(err.message);
debugError(err.stack);
return cb(err, token);
return tokeninfo.prepareToken(accessToken,
function (errorString, token) {
if (errorString) {
return returnError(401, errorString);
}
if(!token.getClientId()) {
return returnError(400, 'Client Id not contained in access token. Giving up!');
}
return token.verify(verificationKey.getCallback(token),
function (err, token) {
var decodedToken = token.getPayload();
if (!decodedToken.zid) {
return returnError(400, 'Identity Zone not contained in access token. Giving up!');
}
if (err) {
debugError(err.statuscode);
debugError(err.message);
debugError(err.stack);
return cb(err, token);
}
var audienceValidator = new JwtAudienceValidator(configArray[0].clientid);
if (configArray[0].xsappname) {
audienceValidator.configureTrustedClientId(configArray[0].xsappname);
}
if (!token.getClientId()) {
return returnError(400, 'Client Id not contained in access token. Giving up!');
}
for(var i=1;i<configArray.length;++i) {
if(configArray[i]) {
if (configArray[i].clientid) {
audienceValidator.configureTrustedClientId(configArray[i].clientid);
if (!decodedToken.zid) {
return returnError(400, 'Identity Zone not contained in access token. Giving up!');
}
if (configArray[i].xsappname) {
audienceValidator.configureTrustedClientId(configArray[i].xsappname);
var audienceValidator = new JwtAudienceValidator(configArray[0].clientid);
if (configArray[0].xsappname) {
audienceValidator.configureTrustedClientId(configArray[0].xsappname);
}
}
}
var valid_result = audienceValidator.validateToken(token.getAudiencesArray(), decodedToken.scope, decodedToken.cid);
if (!valid_result.isValid()) {
return returnError(401, valid_result.getErrorDescription());
}
for (var i = 1; i < configArray.length; ++i) {
if (configArray[i]) {
if (configArray[i].clientid) {
audienceValidator.configureTrustedClientId(configArray[i].clientid);
}
if (configArray[i].xsappname) {
audienceValidator.configureTrustedClientId(configArray[i].xsappname);
}
}
}
if(configArray[0].clientid !== decodedToken.cid) {
foreignMode = audienceValidator.isForeignMode();
}
var valid_result = audienceValidator.validateToken(token.getAudiencesArray(), decodedToken.scope, decodedToken.cid);
if (!valid_result.isValid()) {
return returnError(401, valid_result.getErrorDescription());
}
cb(null, token);
}
);
if (configArray[0].clientid !== decodedToken.cid) {
foreignMode = audienceValidator.isForeignMode();
}
cb(null, token);
}
);
})
};

@@ -235,0 +245,0 @@ };

@@ -425,3 +425,3 @@ 'use strict';

var verificationKey = new VerificationKey(config);
var jwtValidator = new JwtTokenValidator(verificationKey, configArr);
var jwtValidator = new JwtTokenValidator(verificationKey, configArr, config);

@@ -428,0 +428,0 @@ //Now validate the tokens

{
"name": "@sap/xssec",
"version": "3.1.1",
"version": "3.1.2",
"description": "XS Advanced Container Security API for node.js",

@@ -5,0 +5,0 @@ "main": "./lib",

@@ -172,3 +172,7 @@ @sap/xssec: XS Advanced Container Security API for node.js

```
### Support for automatic IAS to XSUAA token conversion
Since verison 3.1.2 it is supported to automatically exchange an incoming IAS token with an XSUAA token, so the token contains scopes like XSUAA applications expect.
For details have a look [here](doc/IAStoXSUAA.md).
### Test Usage without having an Access Token

@@ -326,2 +330,3 @@

also have a look on how to initiate the [token flows](doc/TokenFlows.md) directly
### hasAttributes

@@ -328,0 +333,0 @@

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