Using the SAP Application Router with the SAP Cloud SDK
This guide will show you how to use the SAP Application Router together with the SAP Cloud SDK. You will learn how to secure your application and configure multi-tenancy for principal propagation with an SAP Cloud SDK-based application example powered by nestJS.
SAP Application Router
To enable multi-tenancy for your application, you should use the SAP Application Router. The application router's primary purpose is to be the single entry point of a microservice-based application and act as the application's reverse proxy.
Its responsibilities consist of dispatching requests to backend microservices, authenticating users, and serving static content. The application router checks if a given request has a valid JSON Web Token (JWT) when accessing a target service. If the request contains a valid JWT, the application router forwards the request to the target service; if the request does not contain a valid JWT, the user must authenticate. When using an Identity Provider (IdP) to authenticate, the request is redirected to the IdP where a user gets authenticated and then redirected back to the application router.
Application Router Setup
To deploy your application router in SAP BTP Cloud Foundry, you need to configure it first. Let's walk through the four files you need to use.
The xs-security.json
file defines the security and deployment options for an application.
With the below example, you enable the shared
tenant-mode for your xsuaa
instance, which you need to enable multi-tenancy.
{
"xsappname": "approuter-scaffold",
"tenant-mode": "shared"
}
In the xs-app.json
, you specify to which backend service a request is forwarded to, and how this request has to be authenticated.
You can optionally specify a specific identityProvider
that is used for the authentication.
In the example below, you forward every request against the application router's /
route to the backend destination's /
route.
{
"welcomeFile": "index.html",
"routes": [
{
"source": "/",
"target": "/",
"destination": "approuter-scaffold"
}
]
}
In the package.json
there is only one dependency, the application router module.
{
"name": "approuter",
"dependencies": {
"@sap/approuter": "*"
},
"scripts": {
"start": "node node_modules/@sap/approuter/approuter.js"
}
}
The manifest contains your application router, as well as environment variables which your application router needs for multi-tenancy.
Under env
, you need to specify the TENANT_HOST_PATTERN
and destinations
.
The destinations
are the destinations you use in your xs-app.json
where you forward requests to.
The TENANT_HOST_PATTERN
is a regular expression that describes how a tenant name should be retrieved from the host.
You also have to bind the xsuaa
which you configured with your xs-security.json
to the application router.
applications:
- name: approuter-scaffold-approuter
routes:
- route: approuter-scaffold-apps.cfapps.sap.hana.ondemand.com
path: .
memory: 128M
buildpacks:
- nodejs_buildpack
env:
TENANT_HOST_PATTERN: 'approuter-scaffold-(.*).cfapps.sap.hana.ondemand.com'
destinations: '[{"name":"approuter-scaffold","url":"approuter-scaffold.cfapps.sap.hana.ondemand.com","forwardAuthToken":true}]'
services:
- approuter-scaffold-xsuaa
Securing Your Application
Utilize the passport library to secure your application endpoints. It lets you authenticate endpoints using a JSON web token.
Additionally, the xsenv
library can retrieve your xsuaa
credentials and the xssec
library's JWTStrategy
object for the middleware.
Below is a simple example, where you get the approuter-scaffold-xsuaa
which is bound to your application, use it in the JWTStrategy
, and then forward the middleware to the passport.
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { JWTStrategy } from '@sap/xssec';
import { getServices } from '@sap/xsenv';
import * as passport from 'passport';
const xsuaa = getServices({
xsuaa: { name: 'approuter-scaffold-xsuaa' }
}).xsuaa;
passport.use(new JWTStrategy(xsuaa));
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use(passport.initialize());
app.use(passport.authenticate('JWT', { session: false }));
await app.listen(process.env.PORT || 3000);
}
bootstrap();
Enabling Principal Propagation
To enable principal propagation with this setup, you must forward the request to the endpoints.
First, you forward the request in your app.controller.ts
to the principal propagation endpoint.
@Get('principal-business-partner')
getPrincipalBusinessPartner(
@Req() request: Request,
): Promise<BusinessPartner[]> {
return this.principalBusinessPartnerService.getFiveBusinessPartners(
request,
);
}
Then, you can use the SAP Cloud SDK's retrieveJwt()
function to extract the JWT from your request, and forward it to the execute()
method.
Below is an example using the BusinessPartnerService
showing how to retrieve the top five business partners.
import { Injectable } from '@nestjs/common';
import { retrieveJwt } from '@sap-cloud-sdk/connectivity';
import { Request } from 'express';
import { businessPartnerService } from './generated/business-partner-service';
@Injectable()
export class PrincipalBusinessPartnerService {
async getFiveBusinessPartners(request: Request): Promise<BusinessPartner[]> {
return BusinessPartner.requestBuilder()
.getAll()
.top(5)
.execute({
destinationName: 'MY-DESTINATION',
jwt: retrieveJwt(request)
});
}
}