Secure a Basic Node.js App with the Authorization and Trust Management Service (XSUAA)
- 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
- Download the product list application from this repository or clone the branch sap-tutorial-xsuaa.
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).

- Step 1
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.
- Step 2
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 applicationJavaScriptCopyconst 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:JavaScriptCopyapp.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.jsonfile. In this case the dependencies for the modulespassport,@sap/xsenvand@sap/xssechave to be added.-
Open the
package.jsonfile. -
Add the following dependencies:
JSONCopy"dependencies": { "express": "^4.17.1", "@sap/xsenv": "^3.1.0", "@sap/xssec": "^3.0.10", "passport": "^0.4.1" } -
Save the file.
In the index.js file, which scope is checked for in checkLocalScope()?
-
- Step 3
To use the XSUAA service, a file named
xs-security.jsonis 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:
JSONCopy{ "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. -
- Step 4
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:
JSONCopy{ "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:
JSONCopy{ "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.
-
- Step 5
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:
BashCopyproduct-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.yaml -
- Step 6
In 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.YAMLCopyapplications: # 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: 180 -
Add the binding for the XSUAA service to your application, in the same file.
YAMLCopy... services: - xsuaa-service-tutorial -
Add the configuration data for the approuter:
YAMLCopyapplications: ... # Application Router - name: approuter routes: - route: approuter-product-list-ap25.cfapps.eu10.hana.ondemand.com path: approuter buildpacks: - nodejs_buildpack memory: 128M -
Add the bindings for the XSUAA service to the approuter.
YAMLCopy... services: - xsuaa-service-tutorial -
Add a destination to the approuter.
YAMLCopy# 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:
YAMLCopyapplications: # 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} ] -
- Step 7
Because your are calling the product list over the approuter with
/productsyou need to make a small change within theindex.htmlfile.-
Navigate to the
product-list/myapp/staticfolder. -
Replace line 24 in the
index.htmlfile with the following code.JavaScriptCopyvar productsUrl = "/products/products"; // contains path mapping which is specified in xs-app.json -
Save the file.
-
- Step 8
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.BashCopycf create-service xsuaa application xsuaa-service-tutorial -c security/xs-security.json -
Deploy the application.
BashCopycf push -
- Step 9
Your application has two routes that are defined in the
manifest.yml. The direct route to the application should return an error message sayingunauthorized(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 messageno 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/products -
Enter 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.
-
- Step 10
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.
-
- Step 11
Error message: the hostname of your application is already taken
To resolve this error, please edit the
manifest.ymlfile and change therouteparameter of the product list application or therouteparameter of the approuter (depending on which hostname is already taken). If you change the parameter of the product list application, make sure to change theurlparameter 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.
- XSUAA and the Application Router
- Prepare the application files
- Prepare the application security descriptor
- Prepare the approuter files
- Move static content to the application router
- Update the manifest file
- Update the index.html file
- Create the XSUAA service instance
- Call your application from its secure route
- Assign the role collection
- Troubleshooting
- Resources