@sap/xssec
Advanced tools
Comparing version 2.1.1 to 2.1.6
# Change Log | ||
All notable changes to this project will be documented in this file. | ||
## 2.1.6 - 2018-02-19 | ||
- Update version of module @sap/node-jwt | ||
## 2.1.5 - 2018-02-07 | ||
- Update version of module request | ||
## 2.1.4 - 2017-12-04 | ||
- Support new JWT structure (attribute location ext_cxt) | ||
- First implementation for keycache | ||
## 2.1.3 - 2017-11-29 | ||
- Support for API method getClientId | ||
## 2.1.2 - 2017-10-23 | ||
- Support for API method getSubdomain | ||
## 2.1.1 - 2017-10-09 | ||
@@ -5,0 +26,0 @@ |
@@ -59,1 +59,22 @@ // Scope Prefix: | ||
}); | ||
Object.defineProperty(exports, "KEYCACHE_DEFAULT_TOKENKEY_PATH", { | ||
value: '/token_keys', | ||
enumerable: true, | ||
writable: false, | ||
configurable: false | ||
}); | ||
Object.defineProperty(exports, "KEYCACHE_DEFAULT_CACHE_ENTRY_EXPIRATION_TIME_IN_MINUTES", { | ||
value: 15, | ||
enumerable: true, | ||
writable: false, | ||
configurable: false | ||
}); | ||
Object.defineProperty(exports, "KEYCACHE_DEFAULT_CACHE_SIZE", { | ||
value: 100, | ||
enumerable: true, | ||
writable: false, | ||
configurable: false | ||
}); |
@@ -5,2 +5,3 @@ 'use strict'; | ||
var request = require('request'); | ||
var url = require('url'); | ||
@@ -12,2 +13,11 @@ // use environment variable DEBUG with value 'xssec:*' for trace/error messages | ||
var keycache = require('./keycache'); | ||
// 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); | ||
@@ -51,3 +61,5 @@ debugTrace.log = console.log.bind(console); | ||
this.samlToken = ''; | ||
this.clientId = ''; | ||
this.identityZone = ''; | ||
this.subdomain = null; | ||
this.userAttributes = ''; | ||
@@ -285,3 +297,10 @@ this.additionalAuthAttributes = ''; | ||
debugTrace('\nApplication received a token of grant type "' + result.grant_type + '".'); | ||
var uaaUrl = url.parse(result.iss); | ||
if (uaaUrl.hostname.indexOf('.') === -1) { | ||
self.subdomain = null; | ||
} else { | ||
self.subdomain = uaaUrl.hostname.substring(0, uaaUrl.hostname.indexOf('.')); | ||
} | ||
self.identityZone = result.zid; | ||
self.clientId = result.cid; | ||
self.expirationDate = new Date(result.exp * 1000); | ||
@@ -306,11 +325,27 @@ self.grantType = result.grant_type; | ||
debugTrace('Obtained email: ' + self.userInfo.email); | ||
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)); | ||
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.'); | ||
} | ||
} else { | ||
self.tokenContainsAttributes = false; | ||
debugTrace('\nObtained attributes: no XS user attributes in JWT token available.'); | ||
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.'); | ||
} | ||
} | ||
@@ -342,2 +377,10 @@ } | ||
SecurityContext.prototype.getSubdomain = function() { | ||
return this.subdomain; | ||
}; | ||
SecurityContext.prototype.getClientId = function() { | ||
return this.clientId; | ||
}; | ||
SecurityContext.prototype.getExpirationDate = function() { | ||
@@ -598,3 +641,3 @@ return this.expirationDate; | ||
function offlineValidation(accessToken, config, ssojwt, cb) { | ||
function loadVerificationKey(accessToken, config, cb) { | ||
if (config.verificationkey === undefined) { | ||
@@ -605,3 +648,26 @@ var error = new Error('Error in offline validation of access token, because of missing verificationkey', null); | ||
} | ||
var ssorc = ssojwt.loadPEM(config.verificationkey); | ||
var invalidatedTokenJSON = null; | ||
try { | ||
var invalidatedTokenParts = accessToken.split('.'); | ||
if (invalidatedTokenParts.length !== 3) { | ||
return cb(new Error("Unexpected JWT structure.")); | ||
} | ||
var invalidatedTokenBuffer = new Buffer(invalidatedTokenParts[1], 'base64'); | ||
var invalidatedTokenContent = invalidatedTokenBuffer.toString('ascii'); | ||
invalidatedTokenJSON = JSON.parse(invalidatedTokenContent); | ||
} catch (e) { | ||
return cb(e); | ||
} | ||
if (invalidatedTokenJSON.kid && invalidatedTokenJSON.kid !== 'legacy-token-key') { | ||
return keyCache.getKey(invalidatedTokenJSON.kid, config.url, cb); | ||
} else { | ||
return cb(null, config.verificationkey); | ||
} | ||
} | ||
function checkTokenLocal(accessToken, verificationkey, ssojwt, cb) { | ||
var ssorc = ssojwt.loadPEM(verificationkey); | ||
if ((ssorc !== 0) && (ssorc === 9)) { | ||
@@ -655,2 +721,11 @@ debugTrace('\nSSO library path: ' + process.env['SSOEXT_LIB']); | ||
function offlineValidation(accessToken, config, ssojwt, cb) { | ||
loadVerificationKey(accessToken, config, function(err, verificationkey) { | ||
if (err) { | ||
return cb(err); | ||
} | ||
checkTokenLocal(accessToken, verificationkey, ssojwt, cb); | ||
}); | ||
} | ||
function credentialsReplacer(key, value) { | ||
@@ -657,0 +732,0 @@ if (key === 'clientsecret') { |
@@ -1,1 +0,1 @@ | ||
{"dependencies":{"@sap/node-jwt":"^1.4.1","@sap/xsenv":"^1.2.8","debug":"3.1.0","request":"2.81.0"},"description":"XS Advanced Container Security API for node.js","devDependencies":{"filter-node-package":"^2.0.0","istanbul":"^0.4.5","mocha":"^3.2.0","should":"^7.0.1"},"keywords":["xs"],"main":"./lib","maintainers":[{"name":"https-support.sap.com","email":"do-not-reply@sap.com"}],"name":"@sap/xssec","optionalDependencies":{},"readme":"@sap/xssec: XS Advanced Container Security API for node.js\r\n==========================================================\r\n\r\n## XS Advanced Authentication Primer\r\n\r\nAuthentication for node applications in XS Advanced relies on a special usage of the OAuth 2.0 protocol, which is based on central authentication at the UAA server that then vouches for the authenticated user's identity via a so-called OAuth Access Token. The current implementation uses as access token a JSON web token (JWT), which is a signed text-based token following the JSON syntax.\r\n\r\nNormally, your node application will consist of several parts, that appear as separate applications in your manifest file, e.g. one application part that is responsible for the HANA database content, one application part for your application logic written e.g. in node.js (this is the one that can make use of this XS Advanced Container Security API for node.js), and finally one application part that is responsible for the UI layer (this is the one that may make use of the application router functionality). The latter two applications (the application logic in node.js and the application router) should be bound to one and the same UAA service instance. This has the effect, that these two parts can use the same OAuth client credentials.\r\n\r\nWhen your business users access your application UI with their broser, the application router redirects the browser to the UAA where your business users need to authenticate. After successful authentication, the UAA sends - again via the business user's browser - an OAuth authorization code back to the application router. Now the application router sends this authorization code directly (not via the browser) to the UAA to exchange it into an OAuth access token. If the access token is obtained successfully, the business user has logged on to the UI part of your application already. In order to enable your UI to pass this authentication on to the node.js application part, you need to ensure that the destination to your node.js application part is configured such that the access token is actually sent to the node.js part (\"forwardAuthToken\": true).\r\n\r\nIn order to authenticate this request, which arrives at the node.js backend, sap-xssec offers two mechanisms: Firstly, you can use the XS Advanced Container Security API directly to validate the access token. Secondly, you can make use of the passport strategy that is contained in module sap-xssec as another convenient way how to handle the access token. In the following, both options are described followed by the sap-xssec API description.\r\n\r\nsap-xssec offers an offline validation of the access token, which requires no additional call to the UAA. The trust for this offline validation is created by binding the XS UAA service instance to your application. Inside the credentials section in the environment variable VCAP_SERVICES, the key for validation of tokens is included. By default, the offline validation check will only accept tokens intended for the same OAuth2 client in the same UAA identity zone. This makes sense and will cover the vast majority of use cases. However, if an application absolutely wants to consume token that were issued for either different OAuth2 clients or different identity zones, an Access Control List (ACL) entry for this can be specified in an environment variable named SAP_JWT_TRUST_ACL. The name of the OAuth client is sb-<xsappname from xs-security.json>\r\nThe content is a JSON String, containing an array of identity zones and OAuth2 clients. To trust any OAuth2 client and/or identity zones, an * can be used. For OP, identity zones are not used and value for the identity zone is uaa.\r\n\r\n```JSON\r\nSAP_JWT_TRUST_ACL: [ {\"clientid\":\"<client-id of the OAuth2 client>\",\"identityzone\":\"<identity zone>\"},...]\r\n```\r\n\r\nIf you want to enable another (foreign) application to use some of your application's scopes, you can add a ```granted-apps``` marker to your scope in the ```xs-security.json``` file (as in the following example). The value of the marker is a list of applications that is allowed to request a token with the denoted scope.\r\n\r\n```JSON\r\n{\r\n \"xsappname\" : \"sample-leave-request-app\",\r\n \"description\" : \"This sample application demos leave requests\",\r\n \"scopes\" : [ { \"name\" : \"$XSAPPNAME.createLR\",\r\n \"description\" : \"create leave requests\" },\r\n { \"name\" : \"$XSAPPNAME.approveLR\",\r\n \"description\" : \"approve leave requests\",\r\n \"granted-apps\" : [\"MobileApprovals\"] }\r\n ],\r\n \"attributes\" : [ { \"name\" : \"costcenter\",\r\n \"description\" : \"costcenter\",\r\n \"valueType\" : \"string\"\r\n } ],\r\n \"role-templates\": [ { \"name\" : \"employee\",\r\n \"description\" : \"Role for creating leave requests\",\r\n \"scope-references\" : [ \"$XSAPPNAME.createLR\",\"JobScheduler.scheduleJobs\" ],\r\n \"attribute-references\": [ \"costcenter\"] },\r\n { \"name\" : \"manager\",\r\n \"description\" : \"Role for creating and approving leave requests\",\r\n \"scope-references\" : [ \"$XSAPPNAME.createLR\",\"$XSAPPNAME.approveLR\",\"JobScheduler.scheduleJobs\" ],\r\n \"attribute-references\": [ \"costcenter\" ] }\r\n ]\r\n}\r\n```\r\n\r\n## Usage of the XS Advanced Container Security API in your node.js Application\r\n\r\nIn order to use the capabilities of the XS Advanced container security API, add the module \"sap-xssec\" to the dependencies section of your application's package.json.\r\n\r\nTo enable tracing, you can set the environment variable DEBUG as follows: `DEBUG=xssec:*`.\r\n\r\n### Direct Usage with existing Access Token\r\n\r\nFor the usage of the XS Advanced Container Security API it is necessary to pass a JWT token. If you have such a token, you may use the API as follows. The examples below rely on users and credentials that you should substitute with the ones in your context. The code below is based on version v0.0.9 (if you use another version, the coding might differ).\r\n\r\nThe typical use case for calling this API lies from within a container when an HTTP request is received. In an authorization header (with keyword `bearer`) an access token is contained already. You can remove the prefix `bearer` and pass the remaining string (just as in the following example as `access_token`) to the API.\r\n\r\n```js\r\nxssec.createSecurityContext(access_token, xsenv.getServices({ uaa: 'uaa' }).uaa, function(error, securityContext) {\r\n if (error) {\r\n console.log('Security Context creation failed');\r\n return;\r\n }\r\n console.log('Security Context created successfully');\r\n});\r\n```\r\n\r\nNote that the example above uses module `xsenv` to retrieve the configuration of the default services (which are read from environment variable `VCAP_SERVICES` or if not set, from the default configuration file). However, it passes only the required `uaa` configuration to the method `createSecurityContext`. As default the UAA configuration is searched with tag `xsuaa` by `xsenv`. For details we refer to module @sap/xsenv. The `xsenv` documentation also helps if you want to provide the credentials from e.g. a user provided service.\r\n\r\nThe creation function `xssec.createSecurityContext` is to be used for an end-user token (e.g. for grant_type `password` or grant_type `authorization_code`) where user information is expected to be available within the token and thus within the security context.\r\n\r\n`createSecurityContext` also accepts a token of grant_type `client_credentials`. This leads to the creation of a limited SecurityContext where certain functions are not available. For more details please consult the API description below or your documentation.\r\n\r\n\r\n### Usage with Passport Strategy\r\n\r\nIf you use [express](https://www.npmjs.com/package/express) and [passport](https://www.npmjs.com/package/passport), you can easily plug a ready-made authentication strategy.\r\n\r\n```js\r\nvar express = require('express');\r\nvar passport = require('passport');\r\nvar JWTStrategy = require('@sap/xssec').JWTStrategy;\r\nvar xsenv = require('@sap/xsenv');\r\n\r\n...\r\n\r\nvar app = express();\r\n\r\n...\r\n\r\npassport.use(new JWTStrategy(xsenv.getServices({uaa:{tag:'xsuaa'}}).uaa));\r\n\r\napp.use(passport.initialize());\r\napp.use(passport.authenticate('JWT', { session: false }));\r\n```\r\n\r\nIf JWT token is present in the request and it is successfully verified, following objects are created:\r\n* request.user - according to [User Profile](http://passportjs.org/docs/profile) convention\r\n * id\r\n * name\r\n * givenName\r\n * familyName\r\n * emails `[ { value: <email> } ]`\r\n* request.authInfo - the [Security Context](#api-description)\r\n\r\nIf the `client_credentials` JWT token is present in the request and it is successfully verified, following objects are created:\r\n* request.user - empty object\r\n* request.authInfo - the [Security Context](#api-description)\r\n\r\n#### Session\r\n\r\nIt is recommended to _disable the session_ as in the example above.\r\nIn XSA each request comes with a JWT token so it is authenticated explicitly and identifies the user.\r\nIf you still need the session, you can enable it but then you should also implement [user serialization/deserialization](http://passportjs.org/guide/configure/) and some sort of [session persistency](https://github.com/expressjs/session).\r\n\r\n### Test Usage without having an Access Token\r\n\r\nFor test purposes, you may retrieve the token for a certain user (whose credentials you know) from the UAA as in the following code-snippet.\r\n\r\n```js\r\nvar http = require(\"http\");\r\nvar xssec = require(\"@sap/xssec\");\r\nvar xsenv = require('@sap/xsenv');\r\nvar request = require('request');\r\n\r\nvar uaaService = xsenv.getServices( { uaa: 'uaa' } ).uaa;\r\nvar testService = xsenv.getServices( { test : { label : 'test' } } ).test;\r\nprocess.env.XSAPPNAME = testService.test.xsappname;\r\n\r\nvar options = {\r\n url : uaaService.url + '/oauth/token?client_id=' + uaaService.clientid\r\n + '&grant_type=password&username=' + testService.userid + '&password='\r\n + testService.usersecret\r\n};\r\nrequest.get(\r\n options,\r\n function(error, response, body) {\r\n if (error || response.statusCode !== 200) {\r\n console.log('Token request failed');\r\n return;\r\n }\r\n var json = null;\r\n try {\r\n json = JSON.parse(body);\r\n } catch (e) {\r\n \treturn callback(e);\r\n }\r\n xssec.createSecurityContext(json.access_token, uaaService, function(error, securityContext) {\r\n if (error) {\r\n console.log('Security Context creation failed');\r\n return;\r\n }\r\n console.log('Security Context created successfully');\r\n });\r\n }\r\n).auth(uaaService.clientid, uaaService.clientsecret, false);\r\n```\r\nNote that this example assumes additional test configuration in the file `default_services.json`.\r\n\r\n```\r\n{\r\n \"uaa\": {\r\n \"url\" : \"<UAA URL>\",\r\n \"clientid\" : \"<your application's OAuth client id>\",\r\n \"clientsecret\" : \"<your application's OAuth client secret>\",\r\n \"xsappname\" : \"<your application's name>\",\r\n \"identityzone\" : \"<desired UAA identity zone>\",\r\n \"tags\" : [\"xsuaa\"],\r\n \"verificationkey\" : \"<verification key for offline validation>\"\r\n },\r\n \"test\": {\r\n \"userid\" : \"marissa\",\r\n \"usersecret\" : \"koala\"\r\n }\r\n}\r\n```\r\n\r\n## API Description\r\n\r\n### createSecurityContext\r\n\r\nThis function creates the Security Context by validating the received access token against credentials put into the application's environment via the UAA service binding.\r\n\r\nUsually, the received token must be intended for the current application. More clearly, the OAuth client id in the access token needs to be equal to the OAuth client id of the application (from the application's environment).\r\n\r\nHowever, there are some use cases, when a \"foreign\" token could be accepted although it was not intended for the current application. If you want to enable other applications calling your application backend directly, you can specify in your xs-security.json file an access control list (ACL) entry and declare which OAuth client from which Identity Zone may call your backend.\r\n\r\nParameters:\r\n\r\n* `access token` ... the access token as received from UAA in the \"authorization Bearer\" HTTP header\r\n* `config` ... a structure with mandatory elements url, clientid and clientsecret\r\n* `callback(error, securityContext)`\r\n\r\n### getLogonName\r\n\r\nnot available for tokens of grant_type `client_credentials`, returns the logon name\r\n\r\n### getGivenName\r\n\r\nnot available for tokens of grant_type `client_credentials`, returns the given name\r\n\r\n### getFamilyName\r\n\r\nnot available for tokens of grant_type `client_credentials`, returns the family name\r\n\r\n### getEmail\r\n\r\nnot available for tokens of grant_type `client_credentials`, returns the email\r\n\r\n### checkLocalScope\r\n\r\nchecks a scope that is published by the current application in the xs-security.json file.\r\n\r\nParameters:\r\n\r\n* `scope` ... the scope whose existence is checked against the available scopes of the current user. Here, no prefix is required.\r\n* returns `true` if the scope is contained in the user's scopes, `false` otherwise\r\n\r\n### checkScope\r\n\r\nchecks a scope that is published by an application.\r\n\r\nParameters:\r\n\r\n* `scope` ... the scope whose existence is checked against the available scopes of the current user. Here, the prefix is required, thus the scope string is \"globally unique\".\r\n* returns `true` if the scope is contained in the user's scopes, `false` otherwise\r\n\r\n### getToken\r\n\r\nParameters:\r\n\r\n* `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`).\r\n* `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`.\r\n* 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.\r\n\r\n### getHdbToken\r\n\r\n* returns a token that can be used 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.\r\n\r\n### requestTokenForClient\r\n\r\nRequests 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`.\r\n\r\nParameters:\r\n\r\n* `serviceCredentials` ... the credentials of the service as JSON object. The attributes `clientid`, `clientsecret` and `url` (UAA) are mandatory.\r\n* `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.\r\n* `cb(error, token)` ... callback function\r\n\r\n### hasAttributes\r\n\r\nnot available for tokens of grant_type `client_credentials`.\r\n\r\n* returns `true` if the token contains any xs user attributes, `false` otherwise.\r\n\r\n### getAttribute\r\n\r\nnot available for tokens of grant_type `client_credentials`.\r\n\r\nParameters:\r\n\r\n* `name` ... The name of the attribute that is requested.\r\n* returns the attribute exactly as it is contained in the access token. If no attribute with the given name is contained in the access token, `null` is returned. 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 regardless of whether the requested attribute is contained in the token or not.\r\n\r\n### getAdditionalAuthAttribute\r\n\r\nParameters:\r\n\r\n* `name` ... The name of the additional authentication attribute that is requested.\r\n* returns the additional authentication attribute exactly as it is contained in the access token. If no attribute with the given name is contained in the access token, `null` is returned. Note that additional authentication attributes are also returned in foreign mode (in contrast to getAttribute).\r\n\r\n### isInForeignMode\r\n\r\n* returns `true` if the token, that the security context has been instantiated with, is a foreign token that was not originally issued for the current application, `false` otherwise.\r\n\r\n### getIdentityZone\r\n\r\n* returns the identity zone that the access token has been issued for.\r\n\r\n### getExpirationDate\r\n\r\n* returns the expiration date of the access token as javascript Date object.\r\n\r\n### getCloneServiceInstanceId\r\n\r\n* returns the service instance id of the clone if the XSUAA broker plan is used.\r\n\r\n### getGrantType\r\n\r\n* returns the grant type of the JWT token, e.g. `authorization_code`, `password`, `client_credentials` or `urn:ietf:params:oauth:grant-type:saml2-bearer`.\r\n","readmeFilename":"README.md","repository":{"type":"git"},"scripts":{"prepareRelease":"clean-packages && npm prune --production","test":"make test"},"version":"2.1.1","license":"SEE LICENSE IN developer-license-3.1.txt"} | ||
{"dependencies":{"@sap/node-jwt":"^1.4.5","@sap/xsenv":"^1.2.8","debug":"3.1.0","lru-cache":"4.1.1","request":"2.83.0","valid-url":"1.0.9"},"description":"XS Advanced Container Security API for node.js","devDependencies":{"filter-node-package":"^2.0.0","istanbul":"^0.4.5","mocha":"^3.2.0","should":"^7.0.1"},"keywords":["xs"],"main":"./lib","maintainers":[{"name":"https-support.sap.com","email":"do-not-reply@sap.com"}],"name":"@sap/xssec","optionalDependencies":{},"readme":"@sap/xssec: XS Advanced Container Security API for node.js\r\n==========================================================\r\n\r\n## XS Advanced Authentication Primer\r\n\r\nAuthentication for node applications in XS Advanced relies on a special usage of the OAuth 2.0 protocol, which is based on central authentication at the UAA server that then vouches for the authenticated user's identity via a so-called OAuth Access Token. The current implementation uses as access token a JSON web token (JWT), which is a signed text-based token following the JSON syntax.\r\n\r\nNormally, your node application will consist of several parts, that appear as separate applications in your manifest file, e.g. one application part that is responsible for the HANA database content, one application part for your application logic written e.g. in node.js (this is the one that can make use of this XS Advanced Container Security API for node.js), and finally one application part that is responsible for the UI layer (this is the one that may make use of the application router functionality). The latter two applications (the application logic in node.js and the application router) should be bound to one and the same UAA service instance. This has the effect, that these two parts can use the same OAuth client credentials.\r\n\r\nWhen your business users access your application UI with their broser, the application router redirects the browser to the UAA where your business users need to authenticate. After successful authentication, the UAA sends - again via the business user's browser - an OAuth authorization code back to the application router. Now the application router sends this authorization code directly (not via the browser) to the UAA to exchange it into an OAuth access token. If the access token is obtained successfully, the business user has logged on to the UI part of your application already. In order to enable your UI to pass this authentication on to the node.js application part, you need to ensure that the destination to your node.js application part is configured such that the access token is actually sent to the node.js part (\"forwardAuthToken\": true).\r\n\r\nIn order to authenticate this request, which arrives at the node.js backend, sap-xssec offers two mechanisms: Firstly, you can use the XS Advanced Container Security API directly to validate the access token. Secondly, you can make use of the passport strategy that is contained in module sap-xssec as another convenient way how to handle the access token. In the following, both options are described followed by the sap-xssec API description.\r\n\r\nsap-xssec offers an offline validation of the access token, which requires no additional call to the UAA. The trust for this offline validation is created by binding the XS UAA service instance to your application. Inside the credentials section in the environment variable VCAP_SERVICES, the key for validation of tokens is included. By default, the offline validation check will only accept tokens intended for the same OAuth2 client in the same UAA identity zone. This makes sense and will cover the vast majority of use cases. However, if an application absolutely wants to consume token that were issued for either different OAuth2 clients or different identity zones, an Access Control List (ACL) entry for this can be specified in an environment variable named SAP_JWT_TRUST_ACL. The name of the OAuth client is sb-<xsappname from xs-security.json>\r\nThe content is a JSON String, containing an array of identity zones and OAuth2 clients. To trust any OAuth2 client and/or identity zones, an * can be used. For OP, identity zones are not used and value for the identity zone is uaa.\r\n\r\n```JSON\r\nSAP_JWT_TRUST_ACL: [ {\"clientid\":\"<client-id of the OAuth2 client>\",\"identityzone\":\"<identity zone>\"},...]\r\n```\r\n\r\nIf you want to enable another (foreign) application to use some of your application's scopes, you can add a ```granted-apps``` marker to your scope in the ```xs-security.json``` file (as in the following example). The value of the marker is a list of applications that is allowed to request a token with the denoted scope.\r\n\r\n```JSON\r\n{\r\n \"xsappname\" : \"sample-leave-request-app\",\r\n \"description\" : \"This sample application demos leave requests\",\r\n \"scopes\" : [ { \"name\" : \"$XSAPPNAME.createLR\",\r\n \"description\" : \"create leave requests\" },\r\n { \"name\" : \"$XSAPPNAME.approveLR\",\r\n \"description\" : \"approve leave requests\",\r\n \"granted-apps\" : [\"MobileApprovals\"] }\r\n ],\r\n \"attributes\" : [ { \"name\" : \"costcenter\",\r\n \"description\" : \"costcenter\",\r\n \"valueType\" : \"string\"\r\n } ],\r\n \"role-templates\": [ { \"name\" : \"employee\",\r\n \"description\" : \"Role for creating leave requests\",\r\n \"scope-references\" : [ \"$XSAPPNAME.createLR\",\"JobScheduler.scheduleJobs\" ],\r\n \"attribute-references\": [ \"costcenter\"] },\r\n { \"name\" : \"manager\",\r\n \"description\" : \"Role for creating and approving leave requests\",\r\n \"scope-references\" : [ \"$XSAPPNAME.createLR\",\"$XSAPPNAME.approveLR\",\"JobScheduler.scheduleJobs\" ],\r\n \"attribute-references\": [ \"costcenter\" ] }\r\n ]\r\n}\r\n```\r\n\r\n## Usage of the XS Advanced Container Security API in your node.js Application\r\n\r\nIn order to use the capabilities of the XS Advanced container security API, add the module \"sap-xssec\" to the dependencies section of your application's package.json.\r\n\r\nTo enable tracing, you can set the environment variable DEBUG as follows: `DEBUG=xssec:*`.\r\n\r\n### Direct Usage with existing Access Token\r\n\r\nFor the usage of the XS Advanced Container Security API it is necessary to pass a JWT token. If you have such a token, you may use the API as follows. The examples below rely on users and credentials that you should substitute with the ones in your context. The code below is based on version v0.0.9 (if you use another version, the coding might differ).\r\n\r\nThe typical use case for calling this API lies from within a container when an HTTP request is received. In an authorization header (with keyword `bearer`) an access token is contained already. You can remove the prefix `bearer` and pass the remaining string (just as in the following example as `access_token`) to the API.\r\n\r\n```js\r\nxssec.createSecurityContext(access_token, xsenv.getServices({ uaa: 'uaa' }).uaa, function(error, securityContext) {\r\n if (error) {\r\n console.log('Security Context creation failed');\r\n return;\r\n }\r\n console.log('Security Context created successfully');\r\n});\r\n```\r\n\r\nNote that the example above uses module `xsenv` to retrieve the configuration of the default services (which are read from environment variable `VCAP_SERVICES` or if not set, from the default configuration file). However, it passes only the required `uaa` configuration to the method `createSecurityContext`. As default the UAA configuration is searched with tag `xsuaa` by `xsenv`. For details we refer to module @sap/xsenv. The `xsenv` documentation also helps if you want to provide the credentials from e.g. a user provided service.\r\n\r\nThe creation function `xssec.createSecurityContext` is to be used for an end-user token (e.g. for grant_type `password` or grant_type `authorization_code`) where user information is expected to be available within the token and thus within the security context.\r\n\r\n`createSecurityContext` also accepts a token of grant_type `client_credentials`. This leads to the creation of a limited SecurityContext where certain functions are not available. For more details please consult the API description below or your documentation.\r\n\r\n\r\n### Usage with Passport Strategy\r\n\r\nIf you use [express](https://www.npmjs.com/package/express) and [passport](https://www.npmjs.com/package/passport), you can easily plug a ready-made authentication strategy.\r\n\r\n```js\r\nvar express = require('express');\r\nvar passport = require('passport');\r\nvar JWTStrategy = require('@sap/xssec').JWTStrategy;\r\nvar xsenv = require('@sap/xsenv');\r\n\r\n...\r\n\r\nvar app = express();\r\n\r\n...\r\n\r\npassport.use(new JWTStrategy(xsenv.getServices({uaa:{tag:'xsuaa'}}).uaa));\r\n\r\napp.use(passport.initialize());\r\napp.use(passport.authenticate('JWT', { session: false }));\r\n```\r\n\r\nIf JWT token is present in the request and it is successfully verified, following objects are created:\r\n* request.user - according to [User Profile](http://passportjs.org/docs/profile) convention\r\n * id\r\n * name\r\n * givenName\r\n * familyName\r\n * emails `[ { value: <email> } ]`\r\n* request.authInfo - the [Security Context](#api-description)\r\n\r\nIf the `client_credentials` JWT token is present in the request and it is successfully verified, following objects are created:\r\n* request.user - empty object\r\n* request.authInfo - the [Security Context](#api-description)\r\n\r\n#### Session\r\n\r\nIt is recommended to _disable the session_ as in the example above.\r\nIn XSA each request comes with a JWT token so it is authenticated explicitly and identifies the user.\r\nIf you still need the session, you can enable it but then you should also implement [user serialization/deserialization](http://passportjs.org/guide/configure/) and some sort of [session persistency](https://github.com/expressjs/session).\r\n\r\n### Test Usage without having an Access Token\r\n\r\nFor test purposes, you may retrieve the token for a certain user (whose credentials you know) from the UAA as in the following code-snippet.\r\n\r\n```js\r\nvar http = require(\"http\");\r\nvar xssec = require(\"@sap/xssec\");\r\nvar xsenv = require('@sap/xsenv');\r\nvar request = require('request');\r\n\r\nvar uaaService = xsenv.getServices( { uaa: 'uaa' } ).uaa;\r\nvar testService = xsenv.getServices( { test : { label : 'test' } } ).test;\r\nprocess.env.XSAPPNAME = testService.test.xsappname;\r\n\r\nvar options = {\r\n url : uaaService.url + '/oauth/token?client_id=' + uaaService.clientid\r\n + '&grant_type=password&username=' + testService.userid + '&password='\r\n + testService.usersecret\r\n};\r\nrequest.get(\r\n options,\r\n function(error, response, body) {\r\n if (error || response.statusCode !== 200) {\r\n console.log('Token request failed');\r\n return;\r\n }\r\n var json = null;\r\n try {\r\n json = JSON.parse(body);\r\n } catch (e) {\r\n \treturn callback(e);\r\n }\r\n xssec.createSecurityContext(json.access_token, uaaService, function(error, securityContext) {\r\n if (error) {\r\n console.log('Security Context creation failed');\r\n return;\r\n }\r\n console.log('Security Context created successfully');\r\n });\r\n }\r\n).auth(uaaService.clientid, uaaService.clientsecret, false);\r\n```\r\nNote that this example assumes additional test configuration in the file `default-services.json`.\r\n\r\n```json\r\n{\r\n \"uaa\": {\r\n \"url\" : \"<UAA URL>\",\r\n \"clientid\" : \"<your application's OAuth client id>\",\r\n \"clientsecret\" : \"<your application's OAuth client secret>\",\r\n \"xsappname\" : \"<your application's name>\",\r\n \"identityzone\" : \"<desired UAA identity zone>\",\r\n \"tags\" : [\"xsuaa\"],\r\n \"verificationkey\" : \"<verification key for offline validation>\"\r\n },\r\n \"test\": {\r\n \"userid\" : \"marissa\",\r\n \"usersecret\" : \"koala\"\r\n }\r\n}\r\n```\r\n\r\n## API Description\r\n\r\n### createSecurityContext\r\n\r\nThis function creates the Security Context by validating the received access token against credentials put into the application's environment via the UAA service binding.\r\n\r\nUsually, the received token must be intended for the current application. More clearly, the OAuth client id in the access token needs to be equal to the OAuth client id of the application (from the application's environment).\r\n\r\nHowever, there are some use cases, when a \"foreign\" token could be accepted although it was not intended for the current application. If you want to enable other applications calling your application backend directly, you can specify in your xs-security.json file an access control list (ACL) entry and declare which OAuth client from which Identity Zone may call your backend.\r\n\r\nParameters:\r\n\r\n* `access token` ... the access token as received from UAA in the \"authorization Bearer\" HTTP header\r\n* `config` ... a structure with mandatory elements url, clientid and clientsecret\r\n* `callback(error, securityContext)`\r\n\r\n### getLogonName\r\n\r\nnot available for tokens of grant_type `client_credentials`, returns the logon name\r\n\r\n### getGivenName\r\n\r\nnot available for tokens of grant_type `client_credentials`, returns the given name\r\n\r\n### getFamilyName\r\n\r\nnot available for tokens of grant_type `client_credentials`, returns the family name\r\n\r\n### getEmail\r\n\r\nnot available for tokens of grant_type `client_credentials`, returns the email\r\n\r\n### checkLocalScope\r\n\r\nchecks a scope that is published by the current application in the xs-security.json file.\r\n\r\nParameters:\r\n\r\n* `scope` ... the scope whose existence is checked against the available scopes of the current user. Here, no prefix is required.\r\n* returns `true` if the scope is contained in the user's scopes, `false` otherwise\r\n\r\n### checkScope\r\n\r\nchecks a scope that is published by an application.\r\n\r\nParameters:\r\n\r\n* `scope` ... the scope whose existence is checked against the available scopes of the current user. Here, the prefix is required, thus the scope string is \"globally unique\".\r\n* returns `true` if the scope is contained in the user's scopes, `false` otherwise\r\n\r\n### getToken\r\n\r\nParameters:\r\n\r\n* `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`).\r\n* `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`.\r\n* 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.\r\n\r\n### getHdbToken\r\n\r\n* returns a token that can be used 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.\r\n\r\n### requestTokenForClient\r\n\r\nRequests 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`.\r\n\r\nParameters:\r\n\r\n* `serviceCredentials` ... the credentials of the service as JSON object. The attributes `clientid`, `clientsecret` and `url` (UAA) are mandatory.\r\n* `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.\r\n* `cb(error, token)` ... callback function\r\n\r\n### hasAttributes\r\n\r\nnot available for tokens of grant_type `client_credentials`.\r\n\r\n* returns `true` if the token contains any xs user attributes, `false` otherwise.\r\n\r\n### getAttribute\r\n\r\nnot available for tokens of grant_type `client_credentials`.\r\n\r\nParameters:\r\n\r\n* `name` ... The name of the attribute that is requested.\r\n* returns the attribute exactly as it is contained in the access token. If no attribute with the given name is contained in the access token, `null` is returned. 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 regardless of whether the requested attribute is contained in the token or not.\r\n\r\n### getAdditionalAuthAttribute\r\n\r\nParameters:\r\n\r\n* `name` ... The name of the additional authentication attribute that is requested.\r\n* returns the additional authentication attribute exactly as it is contained in the access token. If no attribute with the given name is contained in the access token, `null` is returned. Note that additional authentication attributes are also returned in foreign mode (in contrast to getAttribute).\r\n\r\n### isInForeignMode\r\n\r\n* returns `true` if the token, that the security context has been instantiated with, is a foreign token that was not originally issued for the current application, `false` otherwise.\r\n\r\n### getSubdomain\r\n\r\n* returns the subdomain that the access token has been issued for.\r\n\r\n### getClientId\r\n\r\n* returns the client id that the access token has been issued for.\r\n\r\n### getIdentityZone\r\n\r\n* returns the identity zone that the access token has been issued for.\r\n\r\n### getExpirationDate\r\n\r\n* returns the expiration date of the access token as javascript Date object.\r\n\r\n### getCloneServiceInstanceId\r\n\r\n* returns the service instance id of the clone if the XSUAA broker plan is used.\r\n\r\n### getGrantType\r\n\r\n* returns the grant type of the JWT token, e.g. `authorization_code`, `password`, `client_credentials` or `urn:ietf:params:oauth:grant-type:saml2-bearer`.\r\n","readmeFilename":"README.md","repository":{"type":"git"},"scripts":{"prepareRelease":"clean-packages && npm prune --production","test":"make test"},"version":"2.1.6","license":"SEE LICENSE IN developer-license-3.1.txt"} |
@@ -161,5 +161,5 @@ @sap/xssec: XS Advanced Container Security API for node.js | ||
``` | ||
Note that this example assumes additional test configuration in the file `default_services.json`. | ||
Note that this example assumes additional test configuration in the file `default-services.json`. | ||
``` | ||
```json | ||
{ | ||
@@ -280,2 +280,10 @@ "uaa": { | ||
### getSubdomain | ||
* returns the subdomain that the access token has been issued for. | ||
### getClientId | ||
* returns the client id that the access token has been issued for. | ||
### getIdentityZone | ||
@@ -282,0 +290,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
185165
10
1024
302
6
+ Addedlru-cache@4.1.1
+ Addedvalid-url@1.0.9
+ Addedajv@5.5.2(transitive)
+ Addedaws-sign2@0.7.0(transitive)
+ Addedboom@4.3.15.3.3(transitive)
+ Addedcryptiles@3.2.1(transitive)
+ Addedfast-deep-equal@1.1.0(transitive)
+ Addedfast-json-stable-stringify@2.1.0(transitive)
+ Addedform-data@2.3.3(transitive)
+ Addedhar-schema@2.0.0(transitive)
+ Addedhar-validator@5.0.3(transitive)
+ Addedhawk@6.0.2(transitive)
+ Addedhoek@4.3.1(transitive)
+ Addedhttp-signature@1.2.0(transitive)
+ Addedjson-schema-traverse@0.3.1(transitive)
+ Addedlru-cache@4.1.1(transitive)
+ Addedperformance-now@2.1.0(transitive)
+ Addedpseudomap@1.0.2(transitive)
+ Addedqs@6.5.3(transitive)
+ Addedrequest@2.83.0(transitive)
+ Addedsntp@2.1.0(transitive)
+ Addedvalid-url@1.0.9(transitive)
+ Addedyallist@2.1.2(transitive)
- Removedajv@4.11.8(transitive)
- Removedassert-plus@0.2.0(transitive)
- Removedaws-sign2@0.6.0(transitive)
- Removedboom@2.10.1(transitive)
- Removedcall-bind@1.0.7(transitive)
- Removedcryptiles@2.0.5(transitive)
- Removeddefine-data-property@1.1.4(transitive)
- Removedes-define-property@1.0.0(transitive)
- Removedes-errors@1.3.0(transitive)
- Removedform-data@2.1.4(transitive)
- Removedfunction-bind@1.1.2(transitive)
- Removedget-intrinsic@1.2.4(transitive)
- Removedgopd@1.0.1(transitive)
- Removedhar-schema@1.0.5(transitive)
- Removedhar-validator@4.2.1(transitive)
- Removedhas-property-descriptors@1.0.2(transitive)
- Removedhas-proto@1.0.3(transitive)
- Removedhas-symbols@1.0.3(transitive)
- Removedhasown@2.0.2(transitive)
- Removedhawk@3.1.3(transitive)
- Removedhoek@2.16.3(transitive)
- Removedhttp-signature@1.1.1(transitive)
- Removedisarray@2.0.5(transitive)
- Removedjson-stable-stringify@1.1.1(transitive)
- Removedjsonify@0.0.1(transitive)
- Removedobject-keys@1.1.1(transitive)
- Removedperformance-now@0.2.0(transitive)
- Removedqs@6.4.1(transitive)
- Removedrequest@2.81.0(transitive)
- Removedset-function-length@1.2.2(transitive)
- Removedsntp@1.0.9(transitive)
Updated@sap/node-jwt@^1.4.5
Updatedrequest@2.83.0