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.3.4 to 3.3.5

3

CHANGELOG.md
# Change Log
All notable changes to this project will be documented in this file.
## 3.3.5 - 2023-09-28
- Support for app2service and app2app for IAS
## 3.3.4 - 2023-09-06

@@ -5,0 +8,0 @@ - Fix IAS token exchange with X509 binding

4

doc/TokenInfo.md

@@ -16,3 +16,3 @@ # TokenInfo

There are several ways to get the TokenInfo.
If an error occurs NO security context is created. But the [TokenInfo](doc/TokenInfo.md) object is available, so you can do some logging.
If an error occurs NO security context is created. But the [TokenInfo](TokenInfo.md) object is available, so you can do some logging.

@@ -125,2 +125,2 @@ #### If you create the SecurityContext on your own

* provide not only verify, but also sign functionality
* easy create tokens for unittesting
* easy create tokens for unittesting

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

getIdentityJwks(url, app_tid, client_id) {
getIdentityJwks(url, serviceCredentials, token) {
if (!url) {

@@ -64,8 +64,13 @@ throw new Error("Cannot get JWKS from empty URL.");

const keyParts = [url, app_tid || ""];
const jwksParams = {
app_tid : token.getAppTID(),
azp: token.getAzp()
}
const keyParts = [url, serviceCredentials.clientid, jwksParams.app_tid, jwksParams.azp];
const replicaKey = this.createCacheKey(keyParts);
if (!this.#replicas.has(replicaKey)) {
const service = new IdentityService(url, app_tid, client_id);
const jwksReplica = new JwksReplica(service, this.expirationTime, this.refreshPeriod);
const service = new IdentityService(serviceCredentials).withCustomDomain(url);
const jwksReplica = new JwksReplica(service, this.expirationTime, this.refreshPeriod).withParams(jwksParams);

@@ -83,3 +88,6 @@ this.#replicas.set(replicaKey, jwksReplica);

return parts.map(s => `${s.length}:${s}`).join(":");
return parts.map(s => {
s ??= "";
return `${s.length}:${s}`;
}).join(":");
}

@@ -86,0 +94,0 @@ }

@@ -15,3 +15,4 @@ 'use strict';

#oAuth2Service; // OAuth2 service from the JWKS is fetched
#cache; // map that holds the JWKS mapped by kid
#params; // params to use when retrieving the JWKS
#keys; // map that holds the JWKS mapped by kid
#jwksUpdate; // promise for ongoing update of JWKS or undefined

@@ -21,2 +22,4 @@ #expirationTime; // default expiration time that will be used for new JWK cache entries

get params() {return this.#params;}
constructor(oAuth2Service, expirationTime = JwksReplica.DEFAULT_EXPIRATION_TIME, refreshPeriod = JwksReplica.DEFAULT_REFRESH_PERIOD) {

@@ -36,3 +39,3 @@ if(!oAuth2Service) {

this.#oAuth2Service = oAuth2Service;
this.#cache = new Map();
this.#keys = new Map();
this.#expirationTime = expirationTime;

@@ -46,5 +49,11 @@ this.#refreshPeriod = refreshPeriod;

/** Configures the replica with parameters that are used when retrieving the JWKS from the server. */
withParams(params) {
this.#params = params;
return this;
}
put(kid, value, expirationTime = this.expirationTime) {
const jwk = new Jwk(kid, value, expirationTime);
this.#cache.set(kid, jwk);
this.#keys.set(kid, jwk);
return jwk;

@@ -54,3 +63,3 @@ }

async get(kid) {
let jwk = this.#cache.get(kid);
let jwk = this.#keys.get(kid);

@@ -60,3 +69,3 @@ if (jwk === undefined || jwk.expired) {

await this.updateJwks();
jwk = this.#cache.get(kid);
jwk = this.#keys.get(kid);

@@ -77,3 +86,8 @@ if (jwk === undefined) {

}
this.updateJwks();
try {
this.updateJwks();
} catch(e) {
debugError("Asynchronous JWKS refresh failed.", e);
}
}

@@ -86,3 +100,3 @@

if (this.#jwksUpdate === undefined) {
this.#jwksUpdate = this.doUpdateJwks();
this.#jwksUpdate = this.#doUpdateJwks();
}

@@ -93,9 +107,9 @@

async doUpdateJwks() {
async #doUpdateJwks() {
debugTrace(`Fetching JWKS from service ${JSON.stringify(this.#oAuth2Service)}.`)
try {
const jwks = await this.#oAuth2Service.fetchJwks();
const jwks = await this.#oAuth2Service.fetchJwks(this.params);
debugTrace(`JWKS received from service ${JSON.stringify(this.#oAuth2Service)}.`)
this.#cache.clear();
this.#keys.clear();
for (let key of jwks) {

@@ -108,2 +122,3 @@ this.put(key.kid, key.value);

debugError(`Error fetching JWKS from service ${JSON.stringify(this.#oAuth2Service)}.`, e);
throw e;
} finally {

@@ -110,0 +125,0 @@ this.#jwksUpdate = undefined;

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

module.exports.fetchOIDCKey = function (serviceCredentialsUrl, app_tid, attributes, cb) {
module.exports.fetchOIDCKey = function (serviceCredentialsUrl, params = {}, cb) {
var options = {

@@ -250,14 +250,7 @@ method: 'GET',

if (app_tid && attributes && attributes.clientId) {
// these two headers must be present both at the same time or not at all to prevent bad requests
options.headers["x-app_tid"] = app_tid;
options.headers["x-client_id"] = attributes.clientId;
}
if (attributes) {
if (attributes.correlationId) {
options.headers[CORRELATIONID_HEADER] = attributes.correlationId;
}
}
params.client_id && (options.headers["x-client_id"] = params.client_id);
params.app_tid && (options.headers["x-app_tid"] = params.app_tid);
params.azp && (options.headers["x-azp"] = params.azp);
params.correlationId && (options.headers[CORRELATIONID_HEADER] = params.correlationId);
return _requestToNetwork(".oidc-jkws", options, cb);

@@ -264,0 +257,0 @@ }

@@ -11,51 +11,52 @@ 'use strict';

class IdentityService {
#url;
#app_tid; // optional zone id
#serviceCredentials;
#domain;
#oidcInfo;
#clientId;
get url() { return this.#url; }
get app_tid() { return this.#app_tid; }
get clientId() { return this.#clientId; }
get serviceCredentials() { return this.#serviceCredentials; }
get url() { return this.#domain; }
constructor(url, app_tid, client_id) {
if (url === undefined) {
throw new Error("IdentityService requires a url.");
constructor(serviceCredentials) {
if (serviceCredentials == null) {
throw new Error("IdentityService requires service credentials.");
}
this.#url = url;
this.#app_tid = app_tid;
this.#clientId = client_id;
this.#serviceCredentials = serviceCredentials;
this.#domain = serviceCredentials.url;
}
async fetchOidcInfo() {
return new Promise((res, rej) => {
try {
requests.requestOpenIDConfiguration(this.url, { clientId: this.clientId }, (err, oidcInfo) => {
if (err) {
return rej(err);
}
return res(oidcInfo);
});
} catch (e) {
return rej(e);
}
});
/** Configures OIDC calls from this service to target the given domain instead of the url from the service credentials. */
withCustomDomain(domain) {
this.#domain = domain;
return this;
}
async fetchJwks() {
return new Promise(async (res, rej) => {
if (!this.#oidcInfo) {
async fetchOidcInfo() {
if (!this.#oidcInfo) {
this.#oidcInfo = await new Promise((res, rej) => {
try {
this.#oidcInfo = await this.fetchOidcInfo();
requests.requestOpenIDConfiguration(this.url, { clientId: this.serviceCredentials.clientId }, (err, oidcInfo) => {
if (err) {
return rej(err);
}
return res(oidcInfo);
});
} catch (e) {
return rej(e);
}
}
});
}
const jwksEndpoint = this.#oidcInfo["jwks_uri"];
return this.#oidcInfo;
}
async fetchJwks(params = {}) {
params.client_id = this.serviceCredentials.clientid;
await this.fetchOidcInfo();
const jwksEndpoint = this.#oidcInfo["jwks_uri"];
return new Promise(async (res, rej) => {
try {
requests.fetchOIDCKey(jwksEndpoint, this.app_tid, { clientId: this.clientId }, (err, json) => {
requests.fetchOIDCKey(jwksEndpoint, params, (err, json) => {
if (err) {

@@ -70,3 +71,3 @@ return rej(err);

key.value = pem;
} catch(e) {
} catch (e) {
debugError(`Could not calculate PEM for key with kid ${key.kid}: ${e}. IdentityService: (${JSON.stringify(this)})`)

@@ -100,6 +101,3 @@ }

return {
...this,
url: this.url,
app_tid : this.app_tid,
client_id: this.clientId
domain: this.domain
}

@@ -106,0 +104,0 @@ }

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

let issuer = payload["ias_iss"] ? payload["ias_iss"] : payload.iss;
if (issuer.indexOf('http') !== 0) {
if (issuer && issuer.indexOf('http') !== 0) {
issuer = "https://" + issuer;

@@ -132,2 +132,6 @@ }

this.getAzp = function() {
return payload.azp;
}
this.isTokenIssuedByXSUAA = function () {

@@ -158,2 +162,2 @@ return payload.ext_attr ? payload.ext_attr.enhancer === "XSUAA" : false;

module.exports = TokenInfo;
module.exports = TokenInfo;

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

try {
const jwks = await jwksManager.getIdentityJwks(issuer, token.getAppTID(), serviceCredentials.clientid);
const jwks = await jwksManager.getIdentityJwks(issuer, serviceCredentials, token);
jwk = await jwks.get(header.kid);

@@ -247,0 +247,0 @@ } catch(e) {

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

@@ -24,6 +24,8 @@ "main": "./lib",

"engines": {
"node": ">=12.0.0"
"node": ">=16.1.0"
},
"devDependencies": {
"@sap/xsenv": "^3.1.1",
"convert": "^4.12.0",
"eslint": "^8.50.0",
"istanbul": "^0.4.5",

@@ -34,4 +36,3 @@ "jwt-decode": "^3.1.2",

"should": "^13.2.1",
"sinon": "^14.0.0",
"convert": "^4.12.0"
"sinon": "^14.0.0"
},

@@ -41,3 +42,3 @@ "dependencies": {

"debug": "^4.3.2",
"jsonwebtoken": "^9.0.0",
"jsonwebtoken": "^9.0.2",
"lru-cache": "^6.0.0",

@@ -44,0 +45,0 @@ "node-rsa": "^1.1.1",

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