This is a sample application that showcases how to build a secure cloud-native Node.js application. The application is secured using the SAP Business Technology Platform Cloud Foundry environment Authorization and Trust Management Service. The current scope of the application is to showcase the following capabilities of the Authorization and Trust Management service.
- Authentication and authorization of users
- Secured service-to-service communications by propagating a business user
- Secured service-to-service communication using a technical user
These features are showcased through an Outbound Freight and Logistics Management Application. This application is based on shipment order creation for freight. As a supplier, he gets different quotations for different modes of transport and he chooses the best available quotation as per his needs. The application is developed based on microservices. The implementation is done using SAP Cloud SDK and Nest.js framework.
- Outbound Freight and Logistics Management
- Description
- Table of content
- Business Scenario
- Architecture
- Requirements
- Security Implementation
- Implementing Authentication and Authorization
- Deploying the application on SAP Business Technology Platform Cloud Foundry Environment
- Demo Script
- Swagger
- Known Issues
- Contributing
- How to Obtain Support
- License
This reference application shows how the conventional freight and logistics process works. There are three services involved here.
- The product and logistics service, which showcases how two services communicate with each other securely. The logistics service checks for product availability and gets the product details.
- The freight manager acts as an external vendor, who provides quotes for shipment, and once the order is placed, the shipment is taken care of by the freight-manager. The communication between freight and logistics service represents technical user authentication.
In the solution diagram we have the following components:
- Product Service
- Logistics Service
- External Freight Manager Service
- Authorization and Trust Management
- HANA Database
This microservice can be used to process products and stock information.
This service communicates securely with the Product Service (using business user propagation) and then consolidates the characteristics of the goods to be shipped. Then the service communicates securely with the freight manager service, which is an external service (using technical user communication) and gets a quotation for the shipment.
This is a service external to the application which is used to calculate the shipment costs using a simple logic. This service is also used in showcasing the app-to-app communication between two microservices deployed in the same subaccount, but bounded to two different Authorization and Trust Management services. For more information, see referencing the application in the documentation for SAP Business Technology Platform.
The global account and subaccounts get their users from identity providers. Administrators make sure that users can only access their dedicated subaccount by making sure that there is a dedicated trust relationship only between the identity providers and the respective subaccounts. Developers configure and deploy application-based security artifacts containing authorizations, and administrators assign these authorizations using the cockpit. Read More
SAP HANA is a high-performance in-memory database that accelerates data-driven, real-time decision making and actions, and supports all workloads, with the broadest advanced analytics on multi-model data, on premise and in the cloud. Read more
-
Install Nodejs
-
Install Visual Studio Code or any of your preferred IDE.
-
Install SAP Cloud SDK CLI globally
npm install -g @sap-cloud-sdk/cli
The SAP Cloud SDK supports you end-to-end when developing applications that communicate with SAP solutions and services such as SAP S/4HANA Cloud, SAP SuccessFactors, and many others.
Using the SDK, you can reduce your effort when developing an application on SAP Business Technology Platform by building on best practices delivered by the SDK. The SDK can provide JavaScript libraries and project templates.
To create such an application, it provides a command-line interface, that allows you to scaffold or enhance an application with the missing parts. To use the SDK: Read More
-
Download and install Cloud Foundry CLI
-
NestJS is a progressive Node.js framework for building efficient and scalable server-side applications, heavily inspired by Angular.
-
The Nest CLI is a powerful tool and can help you create new controllers, modules and interfaces.
-
SAP Business Technology Platform Subaccount with the following entitlements
Service | Plan | Number of Instances |
---|---|---|
SAP HANA Schemas & HDI Containers | hdi-shared | 1 |
SAP Hana Cloud | hana | 1 |
Cloud Foundry Runtime | MEMORY | 1 |
The security implementation in the application is done using the Node-security libraries, which can be integrated with the SAP Business Technology Platform Authorization and Trust Management service as described here. This application implements app-to-app communication so that two microservices can securely communicate with each other. This application showcases how to implement secure communication in two different ways:
- Service communications by propagating a business user
- Service-to-service communication using a technical user
In this approach, the business user is authenticated and his authorizations are used to call another microservice. The user is therefore known to the microservice that it is calling.
In this approach, a technical user is used to access data from an external application. The called application grants the calling application the necessary rights without identifying a user.
Both methods have their use cases, depending on whether or not one need to identify the business user and grant access based on his authorizations or using a technical user might be sufficient.
The steps below describe how authentication and authorization is implemented in the OFLM application.
As a pre prerequisite, the logistics-service and product-service should be bound to same xsuaa instance as they are part of the same application.
-
Add the application security descriptor file (xs-security.json) to the project.
This file can be found in the root folder of the project.
-
Define a role Supplier within the application security descriptor.
Only a person assigned the Supplier role will be able to create a Shipment Order.
-
Configure scope checks for validating JWT tokens.
This is done in the logistics-service and product-service by using passport and xssec packages as described here.
-
Implement app-to-app communication for the business user in the getQuote method of product-service.
When a supplier logs in to create a shipment order, the business user is propagated from the logistics-service to product-service for a stock check before accepting the shipment order. This ensures that enough stock is available before a shipment order is accepted and only a user with the Supplier role has the permission to do a stock check.
Code Snippet Implementation of a scope check can be accessed at the following line
const isAuthorized = req.authInfo.checkLocalScope('Supplier');
// checks for authorization before executing next block, if unauthorized, returns exception
if(isAuthorized){
The authentication token is fetched from request header at the following line
const authorization = req.headers.authorization;
The business user authentication code snippet can be found at the following line
App-to-app communication for the technical user is implemented between the logistics-service and the external freight-manager application using client-credential flow. The logistics-service and the external freight-manager application are bound to different XSUAA instances.
-
The logistics-service is bound to the instance businessuser-authentication(which uses xs-security.json).
-
The external freight-manager service is bound to the instance freight-xsuaa(which uses xs-security.json inside freightmanager directory).
-
The freight-manager service grants a scope to the logistics-service using the property "grant-as-authority-to-apps" in the xs-security.json. This property has the value ["$XSAPPNAME(application,businessuser-authentication)"] where businessuser-authentication is the xs-appname of the businessuser-authentication xsuaa service.
-
The logistics-service accepts the granted authorities. This is achieved by the property "$ACCEPT_GRANTED_AUTHORITIES" in the xs-security.json. This ensures that the freightmanager-service trusts the logistics-service and hence technical user communication between the two services is achieved using client credentials flow.
For more information, refer to section referencing the application in the documentation for SAP Business Technology Platform.
Code snippet of implementation
In xs-security.json
under security-config
folder, the following line is added for establishing trust:
"authorities":["$ACCEPT_GRANTED_AUTHORITIES"]
The xs-security.json
of freight-manager
has the following implementation:
"scopes": [
{
"name": "$XSAPPNAME.Access",
"description": "access freight service ",
"grant-as-authority-to-apps": ["$XSAPPNAME(application,businessuser-authentication)"]
}
]
// Here at "grant-as-authority-to-apps" we pass the xs-app name, XSUAA service-plan of UAA for which we want to establish trust.
Code snippet to generate authentication token for technical user authentication:
To get the client-id
and client-secret
from the bound xsuaa service instance we use the following line of code:
import * as xsenv from '@sap/xsenv';
const xsCredentials = xsenv.cfServiceCredentials({tag: 'xsuaa'});
/**
* exports client credentials from vcap
*/
export const ServiceCredentials = [{ "clientId": xsCredentials.clientid, "clientSecret": xsCredentials.clientsecret,"url": xsCredentials.url }];
The credentials are then used for generating a bearer token which is used for authentication and authorization in the freight-manager.
/**
* This method is used to generate client credentials
* @param clientCredentials XSUAA credentials returned from credentials provider
*/
async getClientToken(clientCredentials: any) {
return new Promise((resolve, reject) => {
const url = `${clientCredentials.url}/oauth/token`;
axios.post(url, `grant_type=client_credentials&client_id=${clientCredentials.clientId}&client_secret=${clientCredentials.clientSecret}`).then((res: any) => {
resolve(res.data.access_token);
}).catch((error: any) => {
console.log(error);
reject(error);
});
});
}
The implementaion in project can be found here
-
Clone the application.
-
Ensure you have cloud foundry CLI installed by typing
cf
in your command-prompt. -
Log on to the Cloud Foundry environment using the following commands at the command prompt:
cf api <api> cf login
api - URL of the Cloud Foundry landscape that you are trying to connect to. Enter username, password, org and space when prompted. For more information, see link.
-
Go to the root of freight-manager, product-service, logistics-service, approuter and install the dependencies.
npm install
-
Open the directory
security-config
in your CLI and create an xsuaa instance with the following command (If you are using the eu10-004 or any other similar regions in your subaccount, please check the Known Issues section before creating the xsuaa instances).cf create-service xsuaa application businessuser-authentication -c xs-security.json
-
Open the directory
freight-manager
in your CLI and create an xsuaa instance with the following command (If you are using the eu10-004 or any other similar regions in your subaccount, please check the Known Issues section before creating the xsuaa instances).cf create-service xsuaa application freight-xsuaa -c xs-security.json
-
Navigate to your SAP Business Technology Platform subaccount and open your development space.
-
For HANA Database instance creation, follow the documentation.
-
Get the database ID using the following command.
cf service <hana_db_instance_name> --guid
-
Give
nodeapp_db
as your HDI schema name. Follow the document for creating a HDI Container. -
Now open the db module in your command prompt and run the following command.
cf push
-
Go to the root directory of
freight-manager
and deploy it using the command:npm run deploy
-
Go to your SAP Business Technology Platform subaccount and navigate to the space where you are deploying the applications.
-
Go to
Service Marketplace
and selectDestination
. -
Go to Instances under destination and create a new instance.
-
Select plan
lite
and give it the namefreight-manager
. -
Once the instance is created, open it. Go to Destinations and choose
New Destination
. -
Give it the name
freight-manager
. Paste the URL of the deployedfreight-manager
service. -
Select
Authentication
asNo Authentication
. Your configuration should look like the following screenshot: -
Go to the root directory of
product-service
and deploy it using the command:npm run deploy
-
Copy the application URL of product-service once its deployed.
-
Open the
manifest.yaml
oflogistics-service
and replaceenv
value with product-service URL. For reference check the screenshot below.
- Open the root directory of
logistics-service
in your command prompt and deploy the application using the following command:npm run deploy
- For deploying the UI and approuter, go to the
approuter
folder. Openmanifest.yaml
. - Replace the values of
product-service
&logistics-service
with the application URL of your deployedproduct-service
andlogistics-service
. Replace theapi-endpoint
in tenant-host-pattern with the API endpoint of your subaccount and deploy the UI using the commandcf push
. - Now launch the deployed approuter application. This will return the error:
Forbidden
, since we don't have roles assigned to the user. We need to create a role collection and assign it to a user. - Go to your subaccount > Role Collections and choose + to create a role collection.
- Give it a name. Now open it and choose Edit.
- Under the Roles tab, choose + to add new role to the role collection.
- From the Dropdown, select Supplier roles.
- Goto Users > + and add your user.
- Now launch the application and you are good to go.
To demo the application, you need to first launch the user interface application.
-
To get the application URL, run the following command:
cf apps
This will give you a list of applications and their URLs. Pick the URL corresponding tooflm-approuter
. To launch the UI, open the link of yourapplication-router
. -
Once the application is launched it will take you to the login page.
-
Enter your credentials and you will be redirected to the application. The Homepage shows list of shipment orders, which were previously created. Here, to get the orders, business user authentication and authorization will take place.
-
To create a new shipment order, choose + at the top-right corner. When you choose +, a dialog opens. You can see list of available products that can be shipped. To get the list of products, business user authentication takes place. The authentication token/bearer token from
logistics-service
is used to authenticate and authorize theproduct-service
. The product list is then returned fromproduct-service
. Select one product from the drop-down. -
After the product is selected, choose the Step-2 button. This takes you to the next section. You can see the selected product. Enter the shipment details like quantity, distance, and preferred mode of transportation. Then choose Request Quote. After choosing Request Quote, the
product-service
is called to check the availability of stock. At this step, business user authentication happens again to check stock. If the stock is there, then the client credentials of UAA bound tologistics-service
, which is thebusinessuser-authentication
UAA is used to generate an authentication token. The generated token is then used in authentication and authorization checks in thefreight-manager
service. -
Once we get the quote price. Choose the Step-3 button. This takes you to the step where you need to enter the delivery details.
-
Enter the address and choose Review. You can see the selected product and its details, quote price, and delivery details. Choose Submit to create the order.
The order is created and you see a success message. Next the order list is uploaded.
The application also exposes a swagger definition for APIs. It can be accessed with the URL as described below:
<service-url>/api/
Authorization You need to generate a bearer token and then use it to access the swagger. The configuration below can be used to generate a bearer token in postman. There's need of client-id, client-secret, and token-url. This information can be found in the xsuaa environment variables.
The token can be provided in swagger. Here's what the swagger definition looks like:
- Unable to find
nest
command.Run
npm install
in the root of all the services. - Unable to find module
request
or any other module.Try deleting the
package-lock.json
and re-deploy. 404
/not-found in logs or UIEnsure that you have correctly mentioned
product-service
URL inlogistics-service
. Ensure you have the correct URL in the destination service. Ensure the application router has the correct URL.- The redirect_uri has an invalid domain.
Before creating the xsuaa instance, please add the below code in the xs-security.json. Replace the eu10-004 with the region that is used.
"oauth2-configuration": { "redirect-uris": [ "https://*.cfapps.eu10-004.hana.ondemand.com/**" ] }
If you wish to contribute code, offer fixes or improvements, please send a pull request. Due to legal reasons, contributors will be asked to accept a DCO when they create the first pull request to this project. This happens in an automated fashion during the submission process. SAP uses the standard DCO text of the Linux Foundation.
In case you find a bug, or you need additional support, please open an issue here in GitHub.
Copyright (c) 2020 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, version 2.0 except as noted otherwise in the LICENSE file.