Secure a Basic Node.js App with the Authorization and Trust Management Service (XSUAA)
Secure a basic single-tenant Node.js application with the Authorization and Trust Management Service (XSUAA).
Overview
You will learn
- How to secure a basic Node.js application with user authentication
- How to secure a certain part of your application with user authorization
- How to assign authorizations (in the form of a role collection) to a user
Prerequisites
Prerequisites
- Download the product list application from this repository or clone the branch sap-tutorial-xsuaa.
Steps
Intro
The goal of this tutorial is to secure and deploy a product list application with authentication and authorization, so only authenticated users with the correct authorizations are able to see the products within the application. Users without the necessary authorizations are able to log in to the application, but do not see the products.
The base for this tutorial is a Node.js application that uses the express framework and SAPUI5 to display a list of products (see screenshot).

To secure this product list application, two components are used. One is called the XSUAA service and the other one is called application router. The application router is used in combination with the XSUAA service to authenticate a user and route the user to the secured application.
The XSUAA plays the role of an OAuth authorization service whereas the application router plays the role of an OAuth client. Furthermore, the application router works as a central entry point to the application. For more information, check the links at the end of this tutorial.
Prepare the index.js file
To prevent a direct call to your application without authentication, it is necessary to add some code to your application. In our example, you use the Node.js passport authentication middlewareΒ andΒ configure it with the XSUAA JWT strategy.
Go to the
product-list/myappfolder.Open the
index.jsfile.To use additional libraries, add the following lines of code below the line
// secure the direct call to the applicationJavaScriptconst passport = require('passport'); const { JWTStrategy } = require('@sap/xssec'); const xsenv = require('@sap/xsenv'); // XSUAA Middleware passport.use(new JWTStrategy(xsenv.getServices({uaa:{tag:'xsuaa'}}).uaa)); app.use(passport.initialize()); app.use(passport.authenticate('JWT', { session: false }));This code prevents direct calls to the product list application without a valid JWT.
To secure the product list with authorization checks, replace the line
app.get('/products', getProducts);in theindex.jsfile with the following code:JavaScriptapp.get('/products', checkReadScope, getProducts); // Scope check function checkReadScope(req, res, next) { if (req.authInfo.checkLocalScope('read')) { return next(); } else { console.log('Missing the expected scope'); res.status(403).end('Forbidden'); } }The
checkReadScopefunction ensures that only a user with the correct authorizations can look at the products.Save the file.
Prepare the package.json file
Since there are now more modules used beside the express module, you have to add the relevant dependencies to your package.json file. In this case the dependencies for the modules passport, @sap/xsenv and @sap/xssec have to be added.
Open the
package.jsonfile.Add the following dependencies:
JSON"dependencies": { "express": "^4.17.1", "@sap/xsenv": "^3.1.0", "@sap/xssec": "^3.0.10", "passport": "^0.4.1" }Save the file.
To use the XSUAA service, a file named xs-security.json is necessary. The file can define properties of the XSUAA service instance as well as different roles and authorizations. In this example the file contains a role template and a role collection with a Product List Viewer role, that enables the user later to view the products.
Add a folder named
securityto yourproduct-listfolder.Within the folder, create a file named
xs-security.json.Add the following content:
JSON{ "xsappname": "product-list", "tenant-mode": "dedicated", "scopes": [ { "name": "$XSAPPNAME.read", "description": "With this scope, USER can read products." } ], "role-templates": [ { "name": "Viewer", "description": "Role to get the list of products", "scope-references": [ "$XSAPPNAME.read" ] } ], "role-collections": [ { "name": "ProductListViewer", "description": "Product List Viewer", "role-template-references": [ "$XSAPPNAME.Viewer" ] } ], "oauth2-configuration": { "redirect-uris": ["https://approuter-product-list-ap25.cfapps.eu10.hana.ondemand.com/login/callback"] } }This creates a role collection with a role template and a role with a reading scope, so a user with this role can view the products. It also adds the redirect URI parameter, which calls the URL of the application router that you will create in the next step. For more information, see Listing Allowed Redirect URIs.
Save the file
To learn more about the
xs-security.jsonfile, check the links at the end of this tutorial.
The approuter will enable you to create a secure route to your application.
Add a folder named
approuterto yourproduct-listfolder.Within that folder create a file named
package.json.Add the following content:
JSON{ "name": "approuter", "dependencies": { "@sap/approuter": "^9.0.2" }, "scripts": { "start": "node node_modules/@sap/approuter/approuter.js" } }Save the file.
In the same folder, create a file named
xs-app.json.Add the following content to that file:
JSON{ "routes": [{ "source": "^/products", "target": "/", "destination": "products-destination" }] }This will create a destination called
products-destination. The destination is later referenced in themanifest.yml.Save the file.
To learn more about the
xs-app.jsonfile, check the links at the end of this tutorial.
For performance reasons it is better to put the images of the application into a static resources folder with the application router.
Navigate to the
approuterfolder.Add a folder named
resources.Move the
imagesfolder frommyapp/static/into theapprouter/resourcesfolder.
By the end of these steps, your folder structure should look like this:
product-list
βββ approuter
βββresources
βββ images
βββ HT-1000.jpg
βββ HT-1010.jpg
βββ HT-1030.jpg
βββ package.json
βββ xs-app.json
βββ myapp
βββ lib
βββ repository.js
βββ products.json
βββ static
βββ index.html
βββ index.js
βββ package.json
βββ security
βββ xs-security.json
βββ manifest.yamlIn the manifest file you have to define a hostname for your application and add a destination. The manifest file is used to bind the XSUAA service instance to your application.
The steps show incrementally what parameters and values have to be added. To avoid indentation errors, you can just copy the whole code at the end.
Navigate to the
product-listfolder.Open the file
manifest.yaml.Give your application a specific host name with the parameter
route. The route has to be unique in the whole Cloud Foundry landscape, so make sure to add a random part to the route, for example your initials and your day of birth, likeproduct-list-ap25andapprouter-product-list-ap25. You also need the route to configure a destination later.YAMLapplications: # Product List Application - name: product-list instances: 1 memory: 128M routes: - route: product-list-ap25.cfapps.eu10.hana.ondemand.com path: myapp buildpacks: - nodejs_buildpack timeout: 180Add the binding for the XSUAA service to your application, in the same file.
YAML... services: - xsuaa-service-tutorialAdd the configuration data for the approuter:
YAMLapplications: ... # Application Router - name: approuter routes: - route: approuter-product-list-ap25.cfapps.eu10.hana.ondemand.com path: approuter buildpacks: - nodejs_buildpack memory: 128MAdd the bindings for the XSUAA service to the approuter.
YAML... services: - xsuaa-service-tutorialAdd a destination to the approuter.
YAML# Application Router ... env: destinations: > [ {"name":"products-destination", "url":"https://product-list-ap25.cfapps.eu10.hana.ondemand.com", "forwardAuthToken": true} ]The
nameparameter is the same as previously defined in the filexs-app.json. theurlparameter is the result of the host name of your application and the region of your Cloud Foundry landscape (https://<hostname>.cfapps.<region>.hana.ondemand.com). TheforwardAuthTokenparameter set to true ensures that the approuter forwards the JWT token to the destination.Ensure that the landscape mentioned in the route is the same as in the previous steps.
Save the file.
When you have completed the steps, your manifest.yml file should look like this:
applications:
# Application
- name: product-list
instances: 1
memory: 128M
routes:
- route: product-list-ap25.cfapps.eu10.hana.ondemand.com
path: myapp
buildpacks:
- nodejs_buildpack
timeout: 180
services:
- xsuaa-service-tutorial
# Application Router
- name: approuter
routes:
- route: approuter-product-list-ap25.cfapps.eu10.hana.ondemand.com
path: approuter
buildpacks:
- nodejs_buildpack
memory: 128M
services:
- xsuaa-service-tutorial
env:
destinations: >
[
{"name":"products-destination",
"url":"https://product-list-ap25.cfapps.eu10.hana.ondemand.com",
"forwardAuthToken": true}
]Because your are calling the product list over the approuter with /products you need to make a small change within the index.html file.
Navigate to the
product-list/myapp/staticfolder.Replace line 24 in the
index.htmlfile with the following code.JavaScriptvar productsUrl = "/products/products"; // Β contains path mapping which is specified in xs-app.jsonSave the file.
Before you can deploy your application, you need to create the service instance for the XSUAA.
Log in to your Cloud Foundry account with the Cloud Foundry CLI.
Navigate to the
product-listfolder.Create the XSUAA service instance with the
xs-security.jsonsecurity descriptor file.Shellcf create-service xsuaa application xsuaa-service-tutorial -c security/xs-security.jsonDeploy the application.
cf pushYour application has two routes that are defined in the manifest.yml. The direct route to the application should return an error message saying unauthorized (because you don’t have a valid JWT yet). The secure route through the approuter redirects to a login screen. After logging in, the application opens but shows the message no data. To see the product data, you need to assign your user the role collection with the necessary authorizations.
First make sure that your application can’t be reached on its direct URL:
https://product-list-ap25.cfapps.eu10.hana.ondemand.comIf everything is working correctly, this will result in an error message reading
unauthorized.Navigate to your application with the secure route of your application router:
https://approuter-product-list-ap25.cfapps.eu10.hana.ondemand.com/productsEnter the e-mail and password of your trial account.
You should see the
no datamessage. This is because you don’t have the role assigned yet to view the products. You will do this in the next step.
Assign your user the role collection that contains the necessary role to view the products in the product list.
Open the SAP BTP cockpit.
Navigate to your subaccount.
Choose the Security tab and choose Role Collections.
Choose the
ProductListViewerrole collection.Edit the list of users in the configuration to include your ID (e-mail address), identity provider, and e-mail address.
Save the
ProductListViewerconfiguration.Call the URL of the approuter again (you might have to delete your cookies/cache before).
https://approuter-product-list-ap25.cfapps.eu10.hana.ondemand.com/productsThe application will now show you the products.
Error message: the hostname of your application is already taken
To resolve this error, please edit the manifest.yml file and change the route parameter of the product list application or the route parameter of the approuter (depending on which hostname is already taken). If you change the parameter of the product list application, make sure to change the url parameter under destinations as well.
500 internal error when calling your application from its secure route
To resolve this error, please check that you have added the bindings for the XSUAA service to the approuter. See Step 6: Substep 6 for more details.
Resources
Discussion
Share feedback on this tutorial or join the conversation in SAP Community.