@sap/approuter
v18.0.0
Published
Node.js based application router
Downloads
96,147
Readme
@sap/approuter
- Overview
- Deploying a business application with microservices
- Working directory
- Configurations
- Destinations
- UAA configuration
- Additional headers configuration
- Additional Cookie Attributes
- Plugins configuration
- Session timeout configuration
- X-Frame-Options configuration
- Cross-Origin Resource Sharing configuration
- Direct Routing URI Patterns configuration
- NodeJS Minimal Logging Level configuration
- Dynamic Identity Provider configuration
- Routes
- Replacements
- xs-app.json configuration file
- welcomeFile property
- authenticationMethod property
- routes property
- login property
- logout property
- destinations property
- services property
- responseHeaders property
- compression property
- pluginMetadataEndpoint property
- whitelistService property
- websockets property
- errorPage property
- cors property
- Complete example of an xs-app.json configuration file
- Headers
- CSRF Protection
- Support of SAP Statistics
- Connectivity
- SaaS Application Registration in SAP BTP
- Authentication with Identity Service (IAS)
- Mutual TLS Authentication (mTLS) and Certificates Handling
- Integration with HTML5 Application Repository
- Integration with Business Services
- Web Sockets
- Session Handling
- External Session Management
- Service to Application Router
- Central Logout
- User API Service
- Allowlist Service
- Scaling
- Sizing Guide for Application Router
- Configure server-side HTTPS
- Audit-Log Service
- Troubleshooting
- Getting Support
- Extending Application Router
- Best practices
Overview
When a business application consists of several different apps (microservices), the application router is used to provide a single entry point to that business application. It has the responsibility to:
- Dispatch requests to backend microservices (reverse proxy)
- Authenticate users
- Serve static content
Let's think of the different apps (microservices) as destinations to which the incoming request will be forwarded. The rules that determine which request should be forwarded to which destination are called routes. For every destination there can be more than one route. You may read more on the concept of routes later in this document. If the backend microservices require authentication, the application router can be configured to authenticate the users and propagate the user information. Again by using routes, the application router can serve static content.
The application router is designed to work in XS Advanced - Cloud Foundry and XS OnPremise Runtime.
A calling component accesses a target service by means of the application router only if there is no JWT token available, for example, if a user invokes the application from a Web browser. If a JWT token is already available, for example, because the user has already been authenticated, or the calling component uses a JWT token for its own OAuth client, the calling component calls the target service directly; it does not need to use the application router.
Note that the application router does not hide the backend microservices in any way. They are still directly accessible bypassing the application router. So the backend microservices must protect all their endpoints by validating the JWT token and implementing proper scope checks. Network isolation is not provided currently by the platform.
Deploying a business application with microservices
For example we can have a business application that has the following structure:
The manifest.yml file is used to deploy the business application on Cloud Foundry and the manifest-op.yml - on the XS OnPremise Runtime. These files should describe all the microservices for that business application.
Folders are used to isolate the different microservices. Let's assume that the application router is the microservice in the web folder (every business application has its own application router). Here is how we can include the application router:
- Manually create the node_modules folder in the web folder.
- Copy and paste the folder that contains the self-contained application router into node_modules. In this example the name of that folder is @sap/approuter, see the start script in the package.json below.
- Check the version of the application router you just copied.
- Create a package.json file in web with content similar to the following and replace the version's value with the version of your application router:
{
"name": "hello-world-approuter",
"dependencies": {
"@sap/approuter": "2.6.1"
},
"scripts": {
"start": "node node_modules/@sap/approuter/approuter.js"
}
}
In order to use the application router you don't have to write any JavaScript code. Only some configurations have to be provided in the web folder. Here is a complete example:
The web folder contains the package.json, node_modules, some configuration files used by the application router, and static resources to be served. You can read more about the configurations later in this document.
By default, the application router runs on port 5000 (if started locally) or it takes the port from the PORT
environment variable.
Working directory
The working directory contains configuration files that the application router needs and static resources that can be served at runtime. In the previous example, the web folder is the working directory. By default the current directory is the working directory. It is possible to configure it during start up of the application router with the following command line argument:
node approuter.js -w <working-dir>
Application router will abort if the working directory does not contain xs-app.json file.
Configurations
The application router makes use of the following configurations:
Main configuration - this is the xs-app.json file. This file is mandatory and contains the main configurations of the application router.
UAA configuration - the application router reads this configuration either from the
VCAP_SERVICES
environment variable (when deployed on Cloud Foundry or XS Advanced OnPremise Runtime) or from the default-services.json file (when running locally). Refer to the documentation of the@sap/xsenv
package for more details.Configurations from the environment - these configurations are either read from the application router's environment (when deployed on Cloud Foundry or XS Advanced OnPremise Runtime) or from the default-env.json file (when running locally). Refer to the documentation of the
@sap/xsenv
package for more details. The environment variables that the application router takes into account are:
Configuration | Environment variable | Description
------------- |-------------------------------------------------| ------------
UAA service name | UAA_SERVICE_NAME
| Contains the name of the UAA service to be used.
Destinations | destinations
| Provides information about the available destinations.
Additional headers | httpHeaders
| Provides headers that the application router will return to the client in its responses.
Additional cookie attributes | COOKIES
| The application router triggers the generation of the following third-party cookies and sends them to the client: • locationAfterLogin • fragmentAfterLogin • signature • JSESSIONID To control the behavior and usage of the third-party cookies, you can configure the attributes SameSite and Partitioned.For more information about third-party cookies, please see KBA 3409306.
Plugins | plugins
| A plugin is just like a route except that you can't configure some inner properties.
Session timeout | SESSION_TIMEOUT
| Positive integer representing the session timeout in minutes. The default timeout is 15 minutes.
X-Frame-Options | SEND_XFRAMEOPTIONS
, httpHeaders
| Configuration for the X-Frame-Options header value.
Allowlist service | CJ_PROTECT_WHITELIST
| Configuration for the allowlist that is preventing clickjack attacks.
Web Sockets origins allowlist | WS_ALLOWED_ORIGINS
| An allowlist configuration that is used for verifying the Origin
header of the initial upgrade request when establishing a web socket connection.
JWT Token refresh | JWT_REFRESH
| The time in minutes before a JWT token expires and the application router should trigger a token refresh routine.
Incoming connection timeout | INCOMING_CONNECTION_TIMEOUT
| Maximum time in milliseconds for a client connection. After that time the connection is closed. If set to 0, the timeout is disabled. Default: 120000 (2 min)
Incoming request timeout | INCOMING_REQUEST_TIMEOUT
| Maximum time in milliseconds for an incoming request. After that time the request is rejected with 408 status. If set to 0, the timeout is disabled. Default: 300000 (5 min)
Tenant host pattern | TENANT_HOST_PATTERN
| String containing a regular expression with a capturing group. The request host is matched against this regular expression. The value of the first capturing group is used as tenant id.
Destination host pattern | DESTINATION_HOST_PATTERN
| String containing a regular expression with a capturing group. The request host is matched against this regular expression. The value of the capturing group is used as destination name.
Compression | COMPRESSION
| Configuration regarding compressing resources before responding to the client.
Secure flag of session cookie | SECURE_SESSION_COOKIE
| Can be set to true
or false
. By default, the Secure flag of the session cookie is set depending on the environment the application router runs in. For example, when application router is behind a router (Cloud Foundry's router or SAP Web Dispatcher) that is configured to serve HTTPS traffic, then this flag will be present. During local development the flag is not set. This environment variable can be used to enforce setting or omitting the Secure flag. Note: If the Secure flag is enforced, the application router will reject requests sent over unencrypted connection (http).
Trusted CA certificates | XS_CACERT_PATH
| List of files paths with trusted CA certificates used for outbound https connections (UAA, destinations, etc.). File paths are separated by path.delimiter. If this is omitted, several well known "root" CAs (like VeriSign) will be used. This variable is set automatically by XSA On-premise runtime.
Reject untrusted certificates | NODE_TLS_REJECT_UNAUTHORIZED
| By default an outbound https connection is terminated if the remote end does not provide a trusted certificate. This check can be disabled by setting NODE_TLS_REJECT_UNAUTHORIZED
to 0
. This is a built-in feature of Node.js. Note: Do not use this in production as it compromises security!
External reverse proxy flag | EXTERNAL_REVERSE_PROXY
| Boolean value that indicates the use of application router behind an external reverse proxy (outside of Cloud Foundry domain)
Skip client credentials tokens load on start | SKIP_CLIENT_CREDENTIALS_TOKENS_LOAD
| Boolean value that indicates that no client credentials tokens should be created during the application router start phase
Cross-Origin Resource Sharing | CORS
| Configuration regarding CORS enablement.
Preserve URL fragment | PRESERVE_FRAGMENT
| When set to true
or not set, fragment part of the URL provided during first request of not logged-in user to protected route will be preserved, and after login flow user is redirected to original URL including fragment part. However, this may break programmatic access to Approuter (e.g. e2e tests), since it introduces change in login flow, which is incompatible with Approuter version 4.0.1 and earlier. Setting value to false
makes login flow backward compatible, however will not take fragment part of the URL into account.
Direct Routing URI Patterns | DIRECT_ROUTING_URI_PATTERNS
| Configuration for direct routing URI patterns.
NodeJS Minimal Logging Level | CF_NODEJS_LOGGING_LEVEL
| Configuration for NodeJS minimal logging level.
Dynamic Identity Provider | DYNAMIC_IDENTITY_PROVIDER
| Configuration for dynamic identity provider.
Backend Cookies Secret | BACKEND_COOKIES_SECRET
| Secret that is used to encrypt backend session cookies in service to Application Router flow. Should be set in case multiple instances of Application Router are used. By default a random sequence of characters is used.
Service to Application Router | SERVICE_2_APPROUTER
| If true
, when the SAP Passport header is received from the application router, it will be transferred without modification to the backend application.
Client certificate header name | CLIENT_CERTIFICATE_HEADER_NAME
| When set application router will use this header name to get the client certificate from the request header in subscription callback. If not provided the default header name x-forwarded-client-cert
is used.
Server Keep Alive | SERVER_KEEP_ALIVE
| server keep alive timeout (positive integer in milliseconds).
Minimum Token Validity | MINIMUM_TOKEN_VALIDITY
| positive integer in seconds. When set, approuter will check that the token returned from the authorization service has an expiration time higher than the minimum token validity value.
State Parameter Secret | STATE_PARAMETER_SECRET
| enables the use of state parameters to prevent CSRF attacks. If this environment variable is set, the application router creates a state parameter for each initial authorization request. By validating that the authentication server returns the same state parameter in its response, the application server can verify that the response did not originate from a third party. Note: this feature is only available in Cloud Foundry runtime
HTTP2 Support | HTTP2_SUPPORT
| Enables the application router to start as an HTTP/2 server. Note: To configure HTTP/2 support, you must use Cloud Foundry routes with an HTTP/2 destination protocol. See Configuring HTTP/2 Support in the Cloud Foundry Documentation. As connection-specific header fields aren't supported by the HTTP/2 protocol, see rfc9113, the application router removes such headers automatically when they are returned from a backend to prevent a failure of the HTTP/2 response.
Store CSRF token in external session | SVC2AR_STORE_CSRF_IN_EXTERNAL_SESSION | If true
and have enabled external session management, the application router can generate and validate CSRF tokens in service-to-application-router flows by storing the token in an external session.
Cache service credentials | CACHE_SERVICE_CREDENTIALS | If true
, services credentials are cached in the application router memory
Enable x-forwarded-host header validation | ENABLE_X_FORWARDED_HOST_VALIDATION | If true
, x-forwarded-host validation will be performed, allowing letters, digits, hypens (-), underscores (_) and dots (.). As well as it validates hostname length.
Add the content security policy headers to the response | ENABLE_FRAME_ANCESTORS_CSP_HEADERS | If true
, Approuter will include the content security policy (CSP) header using subaccount trusted domains with frame-ancestors policy.
Time cache value for frame ancestors CSP header | FRAME_ANCESTORS_CSP_HEADER_CACHE_TIME | Time in seconds for the frame ancestors CSP header to be cached. The default value is 300 seconds.
Store backend session cookies in external session store | STORE_SESSION_COOKIES_IN_EXTERNAL_SESSION_STORE | If true
, the application router will store backend session cookies in an external session store in the service-to-application-router flow. In this case the "ARBE" cookie will not be returned to the calling service.
Own SAP Cloud Service | OWN_SAP_CLOUD_SERVICE | An array that contains the business solutions ("SAP cloud services") that your HTML5 applications are associated with as values. This configuration enables the standalone application router to use the same standardized format for runtime URLs ("/<sap.cloud.service>.-/") that is also used by the managed application router. If a runtime URL contains one of the defined values in the <sap.cloud.service> section, the application router will recognize the value during the processing of a request.
Cookie backward compatibilty | COOKIE_BACKWARD_COMPATIBILITY | If 'true', cookie serialization will preserve previous cookie library version serialization algorithm.
Note: all those environment variables are optional.
Destinations
The destinations configuration can be provided by the destinations
environment variable or by destination service. There has to be a destination for every single app (microservice) that is a part of the business application.
Environment destinations
The destinations configuration is an array of objects. Here are the properties that a destination can have:
Property | Type | Optional | Description
-------- | ---- |:--------:| -----------
name | String | | A unique alphanumeric identifier of the destination.
url | String | | URL of the app (microservice).
proxyHost | String | x | The host of the proxy server used in case the request should go through a proxy to reach the destination.
proxyPort | String | x | The port of the proxy server used in case the request should go through a proxy to reach the destination.
forwardAuthToken | Boolean | x | If true
, the OAuth token is sent to the destination. The default value is false
. This token contains user identity, scopes and other attributes. It is signed by the UAA or IAS service, so it can be used for user authentication and authorization with backend services.
forwardAuthCertificates | Boolean | x | If true
, the certificates and key of the authentication service are added to the HTTP connection to the destination. The default value is false. For more information see: Mutual TLS Authentication (mTLS) and Certificates Handling.
strictSSL | Boolean | x | Configures whether the application router should reject untrusted certificates. The default value is true
.Note: Do not use this in production as it compromises security!
timeout | Number | x | Positive integer representing the maximum wait time for a response (in milliseconds) from the destination. Default is 30000ms.
setXForwardedHeaders | Boolean | x | If true
, the application router adds X-Forwarded-(Host, Path, Proto) headers to the backend request.Default value is true.
proxyType | String | x | Configures whether the destination is used to access applications in on-premise networks or on public Internet. Possible value: OnPremise
. if the property is not provided, it is assumed that it is a public Internet access. Note: if OnPremise
value is set, binding to SAP Cloud Platform connectivity service is required, and forwardAuthToken
property should not be set.
IASDependencyName | String | x | Configures the name of the IAS dependency that is used to exchange the IAS login token. The exchanged token is also forwarded to the backend application.
Note: The timeout specified will also apply to the destination's logout path or service's logout path (if you have set one).
Note: proxyHost
and proxyPort
are optional, but if one of them is defined, then the other one becomes mandatory.
Sample content of the destinations environment variable:
[
{
"name" : "ui5",
"url" : "https://ui5.sap.com",
"proxyHost" : "proxy",
"proxyPort" : "8080",
"forwardAuthToken" : false,
"timeout" : 1200
}
]
It is also possible to include the destinations in the manifest.yml and manifest-op.yml files:
- name: node-hello-world
memory: 100M
path: web
env:
destinations: >
[
{"name":"ui5", "url":"https://ui5.sap.com"}
]
Destination service
Destination configuration can also be read from destination service
.
Here are the Approuter limitations to destination properties configuration from destination service :
Property | Additional Property | Description
-------- |:--------:| -----------
Type | |only HTTP
supported.
Authentication | | All authentication types are supported. Note: User
and Password
are mandatory if the authentication type is basic authentication
.Note: if the authentication type set to principal propagation
the ProxyType have to be on-premise
.Note: if the authentication type set to OAuth2SAMLBearerAssertion
, uaa.user
scope in xs-security.json is required.
ProxyType | | Supported proxy type : on-premise
, internet
, private-link
. Note: if ProxyType set to on-premise
, binding to SAP Cloud Platform connectivity service is required. Note: To check the availability of the private-link proxy type, see SAP Private Link ServiceInformation published on SAP site in the SAP Discovery Center.
Optional additional properties:
Property | Additional Property | Description
-------- |:-------------------:| ---------
HTML5.ForwardAuthToken | x | If true
the OAuth token will be sent to the destination. The default value is false
. This token contains user identity, scopes and other attributes. It is signed by the UAA so it can be used for user authentication and authorization with backend services. Note: if ProxyType set to on-premise
, ForwardAuthToken property should not be set. Note: if Authentication type is other than NoAuthentication, ForwardAuthToken property should not be set.
HTML5.ForwardAuthCertificates | x | If true
, the certificates and key of the authentication service are added to the HTTP connection to the destination. The default value is false. For more information see: Mutual TLS Authentication (mTLS) and Certificates Handling.
HTML5.Timeout | x | Positive integer representing the maximum wait time for a response (in milliseconds) from the destination. Default is 30000ms.Note: The timeout specified will also apply to the destination's logout path or service's logout path (if you have set one).
HTML5.PreserveHostHeader | x | If true
, the application router preserves the host header in the backend request.This is expected by some back-end systems like AS ABAP, which do not process x-forwarded-* headers.
HTML5.DynamicDestination | x | If true
, the application router allows to use this destination dynamically on host or path level.
HTML5.SetXForwardedHeaders | x | If true
, the application router adds X-Forwarded-(Host, Path, Proto) headers to the backend request.Default value is true.
HTML5.IASDependencyName | x | Configures the name of the IAS dependency that is used to exchange the IAS login token. The exchanged token is also forwarded to the backend application.
sap-client | x | If provided, the application router propagates the sap-client and its value as a header in the backend request.This is expected by ABAP back-end systems.
URL.headers.<header-name>
| x | If provided, the application router propagates this special attribute in the destination as the header. The application router can get the headers list from the destination API. Existing request headers are not overwritten.
Note:
- In case destination with the same name is defined both in environment destination and destination service, the destination configuration will load from the environment.
- Destinations on destination service instance level are supported.
- Only destination client certificates of type p12 are supported.
- Only destination trust certificates of the type privacy-enhanced mail (PEM) are supported.
UAA configuration
The User Account and Authentication (UAA) server is responsible for user authentication.
In Cloud Foundry and XS OnPremise Runtime a service is created for this configuration and by using the standard service binding mechanism
the content of this configuration is available in the VCAP_SERVICES
environment variable.
Note: The service should have xsuaa
in its tags or the environment variable UAA_SERVICE_NAME
should be specified (stating the exact name of the UAA service).
During local development the UAA configuration is provided in the default-services.json file.
When the UAA is used for authentication the user is redirected to the UAA's login page to enter their credentials.
Sample content for a default-services.json file:
{
"uaa": {
"url" : "http://my.uaa.server/",
"clientid" : "client-id",
"clientsecret" : "client-secret",
"xsappname" : "my-business-application"
}
}
The application router supports the $XSAPPNAME
placeholder (upper case letters). You may use it in your route configurations in the scope property.
The value of $XSAPPNAME
is taken from the UAA configuration (the xsappname
property).
Additional headers configuration
If configured, the application router can send additional http headers in its responses to the client.
Additional headers can be set in the httpHeaders
environment variable.
Sample configuration for additional headers:
[
{
"X-Frame-Options": "ALLOW-FROM http://localhost"
},
{
"Test-Additional-Header": "1"
}
]
In this case, the application router sends two additional headers in the responses to the client. Custom response headers, configured in the application router configuration file (xs-app.json) are added to the list of additional http headers. If the response header name already exists in the additional http headers list, the value of the response header name overrides the value of the http header.
Caution: For security reasons, the following headers must not be configured: authorization', 'cookie', and 'set-cookie'.
Additional Cookie Attributes
If configured, the application router sends additional cookie attributes in its responses to the client.
The cookie attributes can be set in the COOKIES
environment variable.
Example of configuration for cookies in the manifest.yml :
env:
COOKIES: >
{
"SameSite":"None",
"Partitioned":
{
"supportedPartitionAgents":"^Mozilla.*(Chrome|Chromium|)/((109)|(1[1-9][0-9])|([2-9][0-9][0-9]))",
"unsupportedPartitionAgents":"PostmanRuntime/7.29.2"
}
}
In this example, the application router sets the SameSite attribute of the cookie to "None" and the specifies a Partitioned attribute that is sent in the responses to the client. Note: Currently, only the values "None" and "Lax" are supported for the SameSite attribute. The value "Strict" is not supported. The Partitioned attribute contains two required regular expressions:
supportedPartitionAgents for supported agents
unsupportedPartitionAgents for unsupported agents
You can use a wildcard '(.*)' in supportedAgents to allow all agents to use the Partitioned attribute. Note: unsupportedPartionAgents overwrites the configurations in supportedAgents. If an agent is allowed in supportedPartitionAgents but disallowed in unsupportedPartitionAgents, the Partitioned attribute will not be returned.
Plugins configuration
A plugin serves almost the same purpose as routes. The difference is that plugins can be configured through the environment and that way you can add new routes to the application router without changing the design-time artefact xs-app.json. The plugin configuration properties are the same as those of a route except that you can't configure localDir
, replace
and cacheControl
.
Property | Type | Optional | Description
-------- | ---- |:--------:| -----------
name | String | | The name of this plugin
source | String/Object | | Describes a regular expression that matches the incoming request URL. Note: A request matches a particular route if its path contains the given pattern. To ensure the RegExp matches the complete path, use the following form: ^$. </br> **Note:** Be aware that the RegExp is applied to on the full URL including query parameters.
target | String | x | Defines how the incoming request path will be rewritten for the corresponding destination.
destination | String | | An alphanumeric name of the destination to which the incoming request should be forwarded.
authenticationType | String | x | The value can be ias, xsuaa, basic, or none. The default authenticationType depends on the authentication service binding: If the application router is bound to the Identity Authentication service, the default authenticationType is ias. Otherwise, the default value is xsuaa. If xsuaa or ias are used, the specified authentication server (Identity Authentication or User Account and Authentication) handles the authentication (the user is redirected to the login form of Identity Authentication or User Account and Authentication). The basic authenticationType works with SAP HANA users, SAP ID service, and Identity Authentication service. For more information, see the SAP Note 3015211 - BASIC authentication options for SAP BTP Cloud Foundry applications. If the value none is used, no authentication is required for this route.
</br>.
csrfProtection | Boolean | x | Enable [CSRF protection](#csrf-protection) for this route. The default value is
true.
scope | Array/String/Object | x | Scopes are related to the permissions a user needs to access a resource. This property holds the required scopes to access the target path. Access is granted if the user has at least one of the listed scopes. **Note:** Scopes are defined as part of the xsuaa service instance configuration. You can use
ias as authenticationType and xsuaa scopes for authorization if the application router is bound to both (
iasand
xsuaa`)."
Sample content of the plugins
environment variable:
[
{
"name": "insecurePlugin",
"source": "/plugin",
"destination": "plugin",
"target": "/",
"csrfProtection": false,
"scope": ["viewer", "reader"]
},
{
"name": "publicPlugin",
"source": "/public-plugin",
"destination": "publicPlugin",
"authenticationType": "none"
}
]
Session timeout configuration
For example, if you have the following line in your manifest.yml or manifest-op.yml file:
- name: node-hello-world
memory: 100M
path: web
env:
SESSION_TIMEOUT: 40
After 40 minutes of user inactivity (no requests have been sent to the application router), a Central Logout will be triggered due to session timeout.
Note: The application router depends on the UAA server for user authentication, if the authenticationType
for a route is xsuaa
. The UAA server may have a different session timeout configured.
It is recommended that the configurations of the application router and the UAA are identical.
X-Frame-Options configuration
Application router sends X-Frame-Options
header by default with value SAMEORIGIN
. This behaviour can be changed in 2 ways:
- Disable sending the default header value by setting
SEND_XFRAMEOPTIONS
environment variable tofalse
- Override the value to be sent via additional headers configuration
Cross-Origin Resource Sharing configuration
The CORS keyword enables you to provide support for cross-origin requests, for example, by allowing the modification of the request header. Cross-origin resource sharing (CORS) permits Web pages from other domains to make HTTP requests to your application domain, where normally such requests would automatically be refused by the Web browser's security policy. Cross-origin resource sharing(CORS) is a mechanism that allows restricted resources on a webpage to be requested from another domain (/protocol/port) outside the domain (/protocol/port) from which the first resource was served. CORS configuration enables you to define details to control access to your application resource from other Web browsers. For example, you can specify where requests can originate from or what is allowed in the request and response headers.
The CORS configuration can be provided in the CORS environment variable or in the CORS property of the application router configuration file (xs-app.json). If a cross-origin resource sharing (CORS) configuration exists in both the environment variables and the application router configuration file (xs-app.json), the application router gives priority to the CORS configuration in the application router configuration file.
The CORS configuration is an array of objects. Here are the properties that a CORS object can have:
Property | Type | Optional | Description -------- | ---- |:--------:| ----------- uriPattern| String | | A regular expression representing for which source routes CORS configuration is applicable. To ensure the RegExp matches the complete path, surround it with ^ and $. Defaults: none. allowedOrigin| Array | | A comma-separated list of objects that each one of them containing host name, port and protocol that are allowed by the server.for example: [{?host?: "www.sap.com"}] or [{?host?: ?.sap.com?}]. Note: matching is case-sensitive. In addition, if port or protocol are not specified the default is ?__?. Defaults: none. allowedMethods| Array of upper-case HTTP methods| x | Comma-separated list of HTTP methods that are allowed by the server. Defaults: [?GET?, ?POST?, ?HEAD?, ?OPTIONS?] (all) applies. Note: matching is case-sensitive. maxAge| Number| x | A single value specifying how long, in seconds, a preflight response should be cached. A negative value will prevent CORS Filter from adding this response header to pre-flight response. Defaults: 1800. allowedHeaders| Array of headers| x | Comma-separated list of request headers that are allowed by the serve. Defaults: [?Origin?, ?Accept?, ?X-Requested-With?, ?Content-Type?, ?Access-Control-Request-Method?, ?Access-Control-Request-Headers?]. exposeHeaders| Array of headers| x | Comma-separated list of response headers (other than simple headers) that can be exposed. Defaults: none. allowedCredentials| Boolean| x | A flag that indicates whether the resource supports user credentials. Defaults: true.
Sample content of the CORS environment variable:
[
{
"uriPattern": "^\route1$",
"allowedMethods": [
"GET"
],
"allowedOrigin": [
{
"host": "my_example.my_domain",
"protocol": "https",
"port": 345
}
],
"maxAge": 3600,
"allowedHeaders": [
"Authorization",
"Content-Type"
],
"exposeHeaders": [
"customHeader1",
"customHeader2"
],
"allowedCredentials": true
}
]
It is also possible to include the CORS in the manifest.yml and manifest-op.yml files:
- name: node-hello-world
memory: 100M
path: web
env:
CORS: >
[
{
"allowedOrigin":[
{
"host":"my_host",
"protocol":"https"
}
],
"uriPattern":"^/route1$"
}
]
For route with source that match the REGEX ?^\route1$?, the CORS configuration is enabled.
Direct Routing URI Patterns configuration
With the direct routing URI patterns configuration, you can define a list of URIs that are directed to the routing configuration file (xs-app.json file) of the application router instead of to a specific application's xs-app.json file that is stored in the HTML5 Application Repository. This configuration improves the application loading time and monitoring options because it prevents unnecessary calls to the HTML5 Application Repository.
The configuration is an array of strings or regular expressions. Note that the following regular expressions are preconfigured in the configuration array: "^favicon.ico$", "^login$". Therefore, do not name your HTML5 applications "favicon.ico" or "login"!
You have to provide only the first segment in the URL, after the approuter host. For example, for the URL https://approuter-host/route1/index.html, you enter "route1" in the direct routing URI patterns array.
Sample content of the Direct Routing URI Patterns environment variable:
env:
DIRECT_ROUTING_URI_PATTERNS: >
["route1", "^route2$", "route3"]
NodeJS Minimal Logging Level configuration
With this configuration, you can set the minimal logging level of the cf-nodejs-logging-support library of the application router. The following levels are available:
off
error
warn
info
verbose
debug
silly
The default value is "error".
Here is a sample content for the NodeJS minimal logging level environment variable:
env:
CF_NODEJS_LOGGING_LEVEL: "debug"
Note The application router also uses the @sap/logging library. To configure the log level for this library, you use the XS_APP_LOG_LEVEL environment variable.
Dynamic Identity Provider configuration
If dynamicIdentityProvider is set to true, the end user can set the identity provider (IDP) for the application’s login process. If SAP Authorization and Trust Management is used, the request query parameter sap_idp must be filled with the IDP origin key. If Identity Authentication is used, the sap_idp request query parameter must be filled with the name of the corporate identity provider configured in the administration console. If the IdentityProvider property is defined in the route, its value will be overwritten by the sap_idp query parameter value. The default value for dynamicIdentityProvider is false. This configuration is relevant for a standalone approuter scenario and it is set for all routes.
Here is a sample content for the dynamic identity provider environment variable:
env:
DYNAMIC_IDENTITY_PROVIDER: true
Routes
A route is a configuration that instructs the application router how to process an incoming request with a specific path.
Property | Type | Optional | Description
-------- | ---- |:--------:| -----------
source | String/Object | | Describes a regular expression that matches the incoming request URL. Note: A request matches a particular route if its path contains the given pattern. To ensure the RegExp matches the complete path, use the following form: ^$. </br> **Note:** Be aware that the RegExp is applied to on the full URL including query parameters.
httpMethods | Array of upper-case HTTP methods | x | Which HTTP methods will be served by this route; the methods supported are:
DELETE,
GET,
HEAD,
OPTIONS,
POST,
PUT,
TRACE,
PATCH(no extension methods are supported). If this option is not specified, the route will serve any HTTP method.
target | String | x | Defines how the incoming request path will be rewritten for the corresponding destination or static resource.
destination | String | x | The name of the destination to which the incoming request should be forwarded. The destination name can be a static string or a regular expression that defines how to dynamically fetch the destination name from the source property or from the host.
service | String | x | The name of the service to which the incoming request should be forwarded.
endpoint | String | x | The name of the endpoint within the service to which the incoming request should be forwarded. Can only be used in a route containing a service attribute.
localDir | String | x | Folder in the [working directory](#working-directory) from which the application router will serve static content **Note:** localDir routes support only HEAD and GET requests; requests with any other method receive a 405 Method Not Allowed.
preferLocal | Boolean | x | Defines from which subaccount the destination is retrieved. If preferLocal is true, the destination is retrieved from the provider subaccount. If preferLocal is false or undefined, the destination is retrieved from the subscriber subaccount.
replace | Object | x | An object that contains the configuration for replacing placeholders with values from the environment. *It is only relevant for static resources*. Its structure is described in [Replacements](#replacements).
authenticationType | String | x | The value can be
xsuaa,
ias,
basicor
none. The default one is
ias, if subaccount trusts an ias tenant, else
xsuaa. When
xsuaaor
iasare used the specified authentication server will handle the authentication (the user is redirected to the authentication service login form). The
basicmechanism works with SAP HANA users, SAP ID Service and SAP Identity Authentication service. Find more details in SAP Note 3015211 - BASIC authentication options for SAP BTP Cloud Foundry applications. If
noneis used then no authentication is needed for this route.
csrfProtection | Boolean | x | Enable [CSRF protection](#csrf-protection) for this route. The default value is
true.
scope | Array/String/Object | x | Scopes are related to the permissions a user needs to access a resource. This property holds the required scopes to access the target path.
cacheControl | String | x | String representing the value of the
Cache-Controlheader, which is set on the response when serving static resources. By default the
Cache-Controlheader is not set. *It is only relevant for static resources.*
identityProvider | String | x | The name of the identity provider to use if provided in route’s definition. If not provided, the route will be authenticated with the default identity provider. **Note:** If the authenticationType is set to Basic Authentication or None, do not define the identityProvider property.
dynamicIdentityProvider | Boolean | x | If
dynamicIdentityProvideris
true, the end user can set the identity provider (IDP) for the application’s login process by filling the request query parameter
sap_idpwith the IDP Origin Key. If
IdentityProviderproperty is defined in the route, its value will be overwritten by the
sap_idpquery parameter value. The default value for
dynamicIdentityProvideris
false`.
Note: The properties destination
, localDir
and service
are optional, but exactly one of them must be defined.
Note: When using the property replace
it is mandatory to define the localDir
property.
Note: The cacheControl property is effective only when one of the following settings is performed:
- The localDir property was set
- A service pointing to HTML5 Application Repository ("service": "html5-apps-repo-rt") was set
Example routes
For example, if you have a configuration with the following destination:
[
{
"name" : "app-1",
"url" : "http://localhost:3001"
}
]
Here are some sample route configurations:
- Route with a
destination
and notarget
{
"source": "^/app1/(.*)$",
"destination": "app-1"
}
Since there is no target
property for that route, no path rewriting will take place.
If we receive /app1/a/b as a path, then a request to http://localhost:3001/app1/a/b is sent.
The source path is appended to the destination URL.
- Route with case-insensitive matching
{
"source": {
"path": "^/app1/(.*)$",
"matchCase": false
},
"destination": "app-1"
}
This example is much like the previous one,
but instead of accepting only paths starting with /app1/, we accept any variation of app1's case.
That means if we receive /ApP1/a/B, then a request to http://localhost:3001/ApP1/a/B is sent.
Note: The property matchCase
has to be of type boolean. It is optional and has a default value true
.
- Route with a
destination
and atarget
{
"source": "^/app1/(.*)$",
"target": "/before/$1/after",
"destination": "app-1"
}
- Route with a
service
, atarget
and anendpoint
{
"source": "^/odata/v2/(.*)$",
"target": "$1",
"service": "com.sap.appbasic.country",
"endpoint": "countryservice"
}
When a request with path /app1/a/b is received, the path rewriting is done according to the rules in the target
property.
The request will be forwarded to http://localhost:3001/before/a/b/after.
Note: In regular expressions there is the term capturing group. If a part of a regular expression is surrounded with parenthesis, then what has been matched can be accessed using $ + the number of the group (starting from 1).
In the last example $1 is mapped to the (.*) part of the regular expression in the source
property.
- Route with dynamic
destination
andtarget
{
"source": "^/destination/([^/]+)/(.*)$",
"target": "$2",
"destination": "$1",
"authenticationType": "xsuaa"
}
If you have a another destination configured:
[
{
"name" : "myDestination",
"url" : "http://localhost:3002"
}
]
when a request with the path /destination/myDestination/myTarget is received, the destination will be replaced with the url from "myDestination", the target will get "myTarget" and the request will be redirected to http://localhost:3002/myTarget
Note: You can use a dynamic value (regex) or a static string for both destination and target values
Note: The approuter first looks for the destination name in the manifest.yaml file, and if not found, looks for it in the destination service.
- Destination In Host
For legacy applications that do not support relative URL paths, you need to define your URL in the following way to enable the destination to be extracted from the host the url should be defined in the following way:
https://<tenant>-<destination>.<customdomain>/<pathtofile>
To enable the application router to determine the destination of the URL host, a DESTINATION_HOST_PATTERN attribute must be provided as an environment variable.
Example: When a request with the path https://myDestination.some-approuter.someDomain.com/app1/myTarget is received, the following route is used:
{
"source": "^/app1/([^/]+)/",
"target": "$1",
"destination": "*",
"authenticationType": "xsuaa"
}
In this example, the target will be extracted from the source and the ‘$1’ value is replaced with ‘myTarget’. The destination value is extracted from the host and the ‘*’ value is replaced with ‘myDestination’.
- Route with a
localDir
and notarget
{
"source": "^/web-pages/(.*)$",
"localDir": "my-static-resources"
}
Since there is no target
property for that route, no path rewriting will take place.
If we receive a request with a path /web-pages/welcome-page.html, the local file at my-static-resources/web-pages/welcome-page.html
under the working directory will be served.
- Route with a
localDir
and atarget
{
"source": "^/web-pages/(.*)$",
"target": "$1",
"localDir": "my-static-resources"
}
If we receive a request with a path '/web-pages/welcome-page.html', the local file at 'my-static-resources/welcome-page.html'
under the working directory will be served. Note: The capturing group used in the target
property.
- Route with
localDir
andcacheControl
{
"source": "^/web-pages/",
"localDir": "my-static-resources",
"cacheControl": "public, max-age=1000,must-revalidate"
}
- Route with
service
"html5-apps-repo-rt" andcacheControl
{
"source": "^/index.html$",
"service": "html5-apps-repo-rt",
"authenticationType": "xsuaa",
"cacheControl":"public,max-age=1000,must-revalidate"
}
- Route with
httpMethods
restrictions
The httpMethods
option allows you to split the same path across different targets depending on the HTTP method. For example:
{
"source": "^/app1/(.*)$",
"target": "/before/$1/after",
"httpMethods": ["GET", "POST"]
}
This route will be able to serve only GET and POST requests. Any other method (including extension ones) will get a 405 Method Not Allowed response. The same endpoint can be split across multiple destinations depending on the HTTP method of the requests:
{
"source": "^/app1/(.*)$",
"destination" : "dest-1",
"httpMethods": ["GET"]
},
{
"source": "^/app1/(.*)$",
"destination" : "dest-2",
"httpMethods": ["DELETE", "POST", "PUT"]
}
The setup above will route GET requests to the target dest-1, DELETE, POST and PUT to dest-2, and any other method receives a 405. It is also possible to specify "catchAll" routes, namely those that do not specify httpMethods
restrictions:
{
"source": "^/app1/(.*)$",
"destination" : "dest-1",
"httpMethods": ["GET"]
},
{
"source": "^/app1/(.*)$",
"destination" : "dest-2"
}
In the setup above, GET requests will be routed to dest-1, and all the rest to dest-2.
Why using httpMethods
? It is often useful to split the implementation of microservices across multiple, highly specialized applications. For example, a Java application written to serve high amounts of GET requests that return large payloads is implemented, sized, scaled and load-tested differently than applications that offer APIs to upload limited amounts of data. httpMethods
allows you to split your REST APIs, e.g., /Things to different applications depending on the HTTP methods of the requests, without having to make the difference visible in the URL of the endpoints.
Another usecase for httpMethods
is to "disable" parts of the REST API. For example, it may be necessary to disable some endpoints that accept DELETE for external usage. By allowing only certain methods in the route, you can hide functionalities of your microservice that should not be consumable without having to modify the code or configurations of your service.
Note: localDir
and httpMethods
are incompatible. The following route is invalid:
{
"source": "^/app1/(.*)$",
"target": "/before/$1/after",
"localDir": "resources",
"httpMethods": ["GET", "POST"]
}
However, since localDir
supports only GET and HEAD requests, returning 405 to requests with any other method, any localDir
route is "implicitly" restricted in terms of supported HTTP methods.
- Route with a
scope
An application specific scope has the following format:
<application-name>.<scope-name>
It is possible to configure what scope the user needs to possess in order to access a specific resource. Those configurations are per route.
In this example, the user should have at least one of the scopes in order to access the corresponding resource.
{
"source": "^/web-pages/(.*)$",
"target": "$1",
"scope": ["$XSAPPNAME.viewer", "$XSAPPNAME.reader", "$XSAPPNAME.writer"]
}
For convenience if our route requires only one scope the scope
property can be a string instead of an array. The following configuration is valid as well:
{
"source": "^/web-pages/(.*)$",
"target": "$1",
"scope": "$XSAPPNAME.viewer"
}
You can configure scopes for the different HTTP methods (GET, POST, PUT, HEAD, DELETE, CONNECT, TRACE, PATCH and OPTIONS). If some of the HTTP methods are not explicitly set, the behaviour for them is defined by the default
property. In case there is no default
property specified and the HTTP method is also not specified, the request is rejected by default.
{
"source": "^/web-pages/(.*)$",
"target": "$1",
"scope": {
"GET": "$XSAPPNAME.viewer",
"POST": ["$XSAPPNAME.reader", "$XSAPPNAME.writer"],
"default": "$XSAPPNAME.guest"
}
}
The application router supports the $XSAPPNAME
placeholder. Its value is taken (and then substituted in the routes) from the UAA configuration.
You may read more about it here. Note: The substitution is case sensitive.
You can use the name of the business application directly instead of using the $XSAPPNAME
placeholder:
{
"source": "^/backend/(.*)$",
"scope": "my-business-application.viewer"
}
- Route with an
identityProvider
For example, we can define several identity providers for different types of users. In this example, there are 2 categories: hospital patients and hospital personnel:
- patientsIDP – use for authenticating patients.
- hospitalIDP – use for authenticating all hospital personnel (doctors, nurses etc..).
We can configure 2 routes with the following identityProvider properties:
[
{
"source": "^/patients/sap/opu/odata/(.*)",
"target": "/sap/opu/odata$1",
"destination": "backend",
"authenticationType": "xsuaa",
"identityProvider": "patientsIDP"
},
{
"source": "^/hospital/sap/opu/odata/(.*)",
"target": "/sap/opu/odata$1",
"destination": "backend", "authenticationType": "xsuaa",
"identityProvider": "hospitalIDP"
}
]
So, a patient who tries to log into the system will be authenticated by patientIDP, and a doctor who tries to log in will be authenticated by hospitalIDP.
Note: After logging in using one of the identity providers, to switch to the other one it is necessary to logout and perform a new log in.
Note: Currently, dynamic provisioning of the subscriber account identity provider is not supported.
Note: Identity provider configuration is only supported in the client side login redirect flow.
- Route with a
dynamicIdentityProvider
For example, we can define a route where the value of identityProvider
is patientsIDP
and where dynamic identity provider provisioning is enabled by setting dynamicIdentityProvider
to true
:
[
{
"source": "^/patients/index.html",
"target": "/patients-index.html",
"service": "html5-apps-repo-rt",
"identityProvider": "patientsIDP",
"dynamicIdentityProvider": true
}
]
In this example, the patientsIDP
value for the identityProvider
is replaced by hospitalIDP
if a request with sap_idp=hospitalIDP
is executed, for example, if the request is https://shiva.health-center-approuter.cfapps.hana.ondemand.com/healthreport/patients/index.html?sap_idp=hospitalIDP.
Replacements
This object configures the placeholder replacement in static text resources.
Property | Type | Description
-------- | ---- | -----------
pathSuffixes | Array | An array containing the path suffixes that are relative to localDir
. Only files with a path ending with any of these suffixes will be processed.
vars | Array | A list with the environment variables that will be replaced in the files matching the suffix.
services | Object | An object describing bound services that will provide replacement values. Each property of this object is used to lookup a separate service. The property names are arbitrary. Service lookup format is described in Service Query section in @sap/xsenv documentation.
The supported tags for replacing environment variables are: {{ENV_VAR}}
and {{{ENV_VAR}}}
.
If there is such an environment variable it will be replaced, otherwise it will be just an empty string.
For services you can specify a property from the credentials
section of the service binding which will be replaced.
For example {{{my_service.property}}}
and {{my_service.property}}
Every variable that is replaced using two-brackets syntax will be HTML-escaped.
For example if the value of the environment variable is ab"cd
the result will be ab&quot;cd
.
The triple brackets syntax is used when the replaced values don't need to be escaped and all values will be unchanged.
For example, if somewhere in your xs-app.json you have a route:
{
"source": "^/get/home(.*)",
"target": "$1",
"localDir": "resources",
"replace": {
"pathSuffixes": ["index.html"],
"vars": ["escaped_text", "NOT_ESCAPED"],
"services": {
"my-sapui5-service": {
"tag": "ui5"
}
}
}
}
and you have the following index.html
:
<html>
<head>
<title>{{escaped_text}}</title>
<script src="{{{NOT_ESCAPED}}}/index.js"/>
<script src="{{{my-sapui5-service.url}}}"/>
</head>
</html>
then in index.html
, {{escaped_text}}
and {{{NOT_ESCAPED}}}
will be replaced with the values of the environment variables escaped_text
and NOT_ESCAPED
.
If you have a service in VCAP_SERVICES like:
{
"sapui5_service": [{
"name": "sapui5",
"tags": ["ui5"],
"credentials": {
"url": "http://sapui5url"
}
}]
}
then {{{my-sapui5-service.url}}}
will be replaced with the url
property from sapui5
service - in this case http://sapui5url
.
Note: All index.html files will be processed.
If you want to replace only specific files, you have to set the path of the file relative to localDir
.
Note: All files should be UTF-8 encoded.
Note: If a service is not found an error is thrown on startup.
Note: If a service and an environment variable from vars
have the same name, an error is thrown on startup.
The returned content type is based on the file extension. Currently the supported file extensions are:
- .json - application/json
- .txt - text/plain
- .html - text/html
- .js - application/javascript
- .css - test/css
If the file extension is different, the default content type is text/html
.
Example for pathSuffixes
:
{
"pathSuffixes": [".html"]
}
The suffix .html
means that all files with the extension .html under localDir
and it's subfolders will be processed.
{
"pathSuffixes": ["/abc/main.html", "some.html"]
}
The suffix /abc/main.html
means that all files named main.html which are inside a folder named abc will be processed.
The suffix some.html
means that all files which have a name that ends with some.html will be processed. For example: some.html
, awesome.html
.
{
"pathSuffixes": ["/some.html"]
}
The suffix /some.html
means that all files which have the exact name some.html will be processed. For example: some.html
, /abc/some.html
.
Note: URL path parameters are not supported for replacements. For example, replacements will not work if the path looks like '/test;color=red/index.html'. For more information regarding path parameters refer to http://tools.ietf.org/html/rfc3986#section-3.3.
xs-app.json configuration file
This is the main configuration file of the application router. It contains a JSON object with the following properties:
Property | Type | Optional | Description
-------- | ---- |:--------:| -----------
welcomeFile | String | x | The client is redirected to this page by default, if the request does not have a path. For more information, see welcomeFile.
authenticationMethod | String | x | If set to none
the UAA login roundtrip is disabled. If the property is not set and authentication is defined per route, the value is set to route
by default.
sessionTimeout | Number | x | Used to set session timeout. The default is 15 minutes. If the SESSION_TIMEOUT environment variable is set this property will be overwritten.
routes | Array | x | Contains all route configurations. The position of a configuration in this array is of significance for the application router in case a path matches more than one source. The first route whose source matches the path of the incoming request gets activated.
login | Object | x | Contains the configuration for the endpoint of the application router which will be used by the UAA during the OAuth2 authentication routine. By default this endpoint is /login/callback
.
logout | Object | x | Provides options for a Central Logout endpoint and a page to which the client to be redirected by the UAA after logout.
destinations | Obje