Skip to Content

INTRODUCTION

User account and authentication(UAA) is the process by which an application identifies a user, verifies his/her identity and then checks if he/she has the required permissions to access the application. This process is essential in ensuring the security of an application.

In this blog post, we explain the UAA setup process for an application that has its back-end as a Java application and the front-end/UI as a UI5 application. We begin with explaining the basics of the UAA process – the terminology, the services, the protocols and the flow. Following this, we look into the installation and setup of the Application Router and the configurations required in the Identity Provider to setup the user authorization. Lastly, we explain the process of securing the back-end service through assertions and how to access the same from the front-end/UI.

 

Identification vs. Authentication vs. Authorization

Every application must be able to:

  • identify a user (to be able to hold user data and distinguish different users by a unique ID)
  • authenticate a user (to be able to prove the identity of a user based on a user-secret shared with the application)
  • assign authorizations to a user and enforce these (to be able to define, assign and enforce policies of system-usage)

Terminology: 

  • Identification – Making a claim about who you are (via username, e-mail, etc.)
  • Authentication – Proving you are who you claim to be (via password, certificate, token, single sign-on, etc.) SAML 2.0 is protocol used for authentication.
  • Authorization – Authorization is the function of specifying access rights/privileges to resources. Authorization allows what you can do on the system, once you have been authenticated. OAuth 2.0 is the protocol used for authorization.

AppRouter: Instead of letting the customer access this application directly, we will use the Application Router (App Router) which serves the following purposes.

  1. App Router is a general entry point into the application- routing requests to the required services/routes.
  2. App Router is responsible for managing authentication flows. The App Router takes incoming, unauthenticated requests from users and initiates an OAuth2 flow with the XSUAA
  3. App Router handles multi-tenancy in applications. Each multi-tenant application has to deploy its own application router, and the application router handles requests of all tenants to the application. Refer here for more details of the role of App Router in handling multi-tenancy

The following services are used for UAA:

Service Provider: Issues access tokens for the client to obtain the authorizations of the resource owner after he was successfully authenticated by a SAML 2.0 compliant identity provider. An access token represents credentials used to access protected resources.

Identity Provider:  Identity provider used for authenticating user.

The following protocols are used for UAA:

SAML 2.0: The Security Assertion Markup Language (SAML 2.0) is an open standard based on XML for exchanging authentication and authorization data of a principal (user) between an identity provider (IdP) and a service provider (SP).

See here for further details about SAML 2.0

OAuth 2.0: The OAuth  2.0 specification defines a delegation protocol that is useful for conveying authorization decisions. OAuth is used in a wide variety of applications, including providing mechanisms for user authentication.

See here for further details about OAuth 2.0

 

JWT Token:

JSON Web Token (JWT) (RFC 7519) is an emerging open standard that defines a compact token format for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed with the private key of the authorization server (UAA service).

 

Authentication sequence in XS-UAA

A combination of SAML 2.0 and OAuth 2.0 was chosen to implement authentication and authorization for XSA security.

The sequence flow of the authentication and authorization assignment process is depicted in the next diagram:

  1. The browser sends its first request to the business app and hits the approuter. The approuter considers this as the initial request, because a session id is not included.
  2. The approuter redirects the request to the XSA UAA component. The URL for the redirect is taken from the variable VCAP_SERVICES of the approuter environment.
  3. The browser sends the request to the XSA UAA component. The XSA UAA considers this as the initial request, because a JWT token is not included.
  4. The XSA UAA redirects the request to the SAML 2.0 IdP. The URL for the redirect is taken from the configuration which was maintained when the trusted relationship between XSA UAA and SAML 2.0 IdP was set up.
  5. The browser sends the request to the SAML 2.0 IdP. The SAML 2.0 IdP considers this as the initial request, because authentication was not provided.
  6. The SAML 2.0 IdP responds with a login page.
  7. The user enters his credentials and sends a request with his credentials.
  8. The SAML 2.0 IdP authenticates the user, creates a SAML 2.0 Bearer Assertion and includes it in the response. The response is redirected back to the XSA UAA.
  9. The browser sends the request with the SAML 2.0 Bearer Assertion to the XSA UAA component. XSA UAA considers this request as authenticated and uses the information of the Bearer Assertion to create a JWT Token and an “Authorization Code”. XSA UAA keeps the JWT Token and includes the Authorization Code in the response. The response is redirected to the approuter.
  10. The browser sends the request with the “Authorization Code” to the approuter. The approuter considers this request as authenticated and uses the “Authorization Code” to request the JWT Token from XSA UAA. The approuter uses his “clientid” and “clientsecret” to authenticate itself against XSA UAA.
  11. The XSA UAA component retrieves the JWT Token which was associated to the “Authorization Code” and includes it in the response.
  12. The approuter has now the JWT Token. It creates a session id and assigns it to the JWT Token. The JWT Token is added to the browser request and forwarded to the micro service.
  13. The approuter includes the session id in the response and routes the response back to the browser.
  14. Subsequent browser requests include the session id. The approuter derives the JWT Token from the session id, adds it to the subsequent request and forwards the request to the micro service.

In this flow it is important to notice that the JWT Token never appears in the browser. This is achieved by the “Authorization Code”: with it, the user “authorizes” the approuter to obtain the authorizations – the JWT Token – from the XSA UAA component. The browser never needs to know about the authorizations, because the approuter enriches each subsequent request with the JWT Token, before the request is routed to the micro service.

 

APPROUTER

INSTALLATION:

The App Router can be installed in two different ways: (1) Download from Service Marketplace and (2) Download from the SAP NPM Registry. These steps are explained below:

Alternative 1: Get the App Router via Service Marketplace

  1. Before you can start the setup and configuration of the App Router component you need to download the XSA Javascript package from Service Marketplace: https://launchpad.support.sap.com/#/softwarecenter/template/products/%20_APP=00200682500000001943&_EVENT=DISPHIER&HEADER=Y&FUNCTIONBAR=N&EVENT=TREE&NE=NAVIGATE&ENR=73554900100200003885&V=MAINT&TA=ACTUAL&PAGE=SEARCH/XS%20JAVASCRIPT%201. At the time of writing the package XS_JSCRIPT14_3-70001363.ZIP is the most recent one.
  2. After downloading the package extract it to your favorite <location>
  3. cd <location>/@sap
  4. Copy the approuter directory to some newly created directory that we call <destLocation>

Alternative 2: Get the App Router via SAP NPM Registry

This alternative approach requires that you have npm installed on your machine (already installed if you using vagrant image for your vm).

  1. Go to your favourite <destLocation> and create the approuter directory
cd <destLocation>mkdir approutercd approuter

2. Place the following package.json in your approuter directory.

{
  "name": "approuter",
  "dependencies": {
    "@sap/approuter": "*"
  },
  "scripts": {
    "start": "node node_modules/@sap/approuter/approuter.js"
  }
}

It contains name of approuter,dependencies,start point of approuter application

3. Install AppRouter dependencies using the following commands

npm config set @sap:registry https://npm.sap.com
npm install

 

SETUP:

  1. Within <destLocation>/approuter create a new file called xs-app.json with the following content
{
  "routes": [
  {
  "source": "/service",
    "target": "/",
    "destination": "service-destination"
  },
  {
  "source": "/",
    "target": "/",
    "destination": "ui-destination"
  }]
}

The xs-app.json file contains routes to backend service.

/service can be used for your backing service (java service layer)
All references to your service from your UI or any other host will have to prefix /service to the controller path.

If you have multiple UI5 applications, then you can configure them with different source endpoints and “/” as the target for each.

{
  "routes": [
  {
  "source": "/service/",
    "target": "/",
    "destination": "service-destination"
  },
  {
  "source": "/ui1/",
    "target": "/",
    "destination": "ui1-destination"
  },
  {
  "source": "/ui2/",
    "target": "/",
    "destination": "ui2-destination"
  }]
}
  1. Within <destLocation> create a new yml file for the AppRouter microservice with the following content as reference. Be sure to check the validity of your manifest file using http://www.yamllint.com/.
--- 
applications: 
  - 
    buildpack: nodejs_buildpack
    env: 
      SAP_JWT_TRUST_ACL: "[{\"clientid\" : \"*\", \"identityzone\" : \"*\"}]"
      TENANT_HOST_PATTERN: ^(.*)- hostname.cfapps.sap.hana.ondemand.com
      destinations: "[{\"name\":\"service-destination\", \"url\" :\"https://--------.cfapps.sap.hana.ondemand.com\", \"forwardAuthToken\": true}, {\"name\":\"ui-destination\", \"url\" :\"https://----.cfapps.sap.hana.ondemand.com\", \"forwardAuthToken\": true}]"
    host: hostname
    memory: 128M
    name: approuterName
    path: approuter
    services: 
      - yourxsuaaservicename

Manifest.yml file contains information about credentials of approuter, tenant host pattern variable, destinations, hostname, memory,  path and services.

Variables/sections description used in manifest.yml file:

  • The TENANT_HOST_PATTERN is a variable that declares the pattern how multiple tenants in the URL are identified and handled.
  • SAP_JWT_TRUST_ACL represents credentials for approuter.
  • Destinations is a variable that declares the internal routes from the App Router to the underlying backend microservices.This app-destination is referenced by the previously created xs-app.json file.
  • The Services section declares to bind our own XSUAA service instance to the App Router. This binding will ensure a corresponding VCAP_SERVICE entry that holds the client ID, client secret and public key that is required to validate any incoming OAuth token/JWT from the XSUAA service.
  1. Now we need to create a service binding to the XSUAA service. As a prerequisite we require a xs-security.json(security descriptor) file that contains a declaration about authorization scopes we intend to use in our application. We put this file to <destLocation>/xs-security.json.
{
    "xsappname": "xsuaaDemo",
    "tenant-mode": "shared",
    "scopes": [
        {
            "name": "$XSAPPNAME.Scope1",
            "description": ""
        },
        {
            "name": "$XSAPPNAME.Scope2",
            "description": ""
        }
  ],
    "role-templates": [
    {
            "name": "Role1",
            "description": "",
            "scope-references": [
             "$XSAPPNAME.Scope1"
              ] 
              
    },
    {
            "name": "Role2",
            "description": "SOC Administrator Role Template",
            "scope-references": [
             "$XSAPPNAME.Scope2"
              ] 
              
              
    }
  ]
}

Note 1: The xsappname has to be unique within the entire XSUAA instance.

Note 2: the variable  tenant-mode:shared assumes a multi-tenant application and will require the TENANT_HOST_PATTERN variable to be declared. You may also use “tenant-mode”: “dedicated” if you develop a single-tenant application.
Here we have defined two scopes and two roles respective to these scopes. Our Java application endpoints can be protected using scopes only and the application role builder at the tenant end only recognizes role templates. This is the reason for creating two role collections for both scopes. Later, we will map the groups that we will shortly create in the IdP (Identity Provider) to these role collections/templates.

  1. Navigate to the location <destLocation>

We then create a service instance called yourxsuaaservicename of the XSUAA service by issuing the following command and using the xs-security.json file:

cf create-service xsuaa application yourxsuaaservicename -c xs-security.json

Note : To activate the multi-tenancy mode, the uaa instances of the applications MUST use the service plan “application”.

  1. Ensure that xs-app.json and json and the node_modules directory with content are present in <destLocation>
  2. Then deploy the AppRouter using the following (with the appropriate API endpoint of your Cloud Foundry region):
cd <destLocation>
cf api https://api.cf.eu10.hana.ondemand.com
cf login
cf push
  1. Map the approuter route to repective tenant. Using following command.
cf map-route approuterName cfapps.sap.hana.ondemand.com -n tenantName-hostname

 

Configuration in Identity Provider and Tenant:

Admin Configurations-

  1. To create groups and users in your Identity Provider, we will need access to the Administration Console of SAP Cloud Identity (SCI). Link for how to do this :
  2. Setup the two-way trust between the tenant and your identity provider following these steps. In most cases, an existing tenant and identity provider and the configurations will be present.
  3. You need to have your application users in this identity provider in order to assign them to their respective groups. Each user can register here.

Alternatively, users can also be created using User Management.

  1. Login to the admin console and setup the required user groups.

  1. Assignment of users to groups is done using User Management. Head over to the user in user management and then assign them the group using User Groups tab.

  1. In order to get access to Application Role Builder and SAML IDP the user needs to be assigned to the admin user group.
  2. Click on Application Role Builder and go to Applications tab and search for the application name as defined in xs-security.json in the approuter in the “xsappname” . It will be appended with !t37.

  1. Check whether the Scopes and Roles are same as you defined in the xs-security.json file
  2. Click on Role Collection tab from the sidebar and create a role collection that will be ultimately mapped to the user group after being mapped to the application roles we specified in the xs-security.json which are in turn mapped to the scopes.

10. Click on the newly created role collection in the Role Collections tab. Go to the roles section. Add Application Role. Choose you xsappname!t37 and then chose the respective Application role that will be SOMAdmin in your case.

  1. Similarly create another role collection and add application role to the newly created role collection.
  2. Now go back to the admin page and chose SAML IDP (https://sap-icbs.admin.cfapps.sap.hana.ondemand.com/saml/index.html) . Here we map the Role Collection we created to the Groups from the Identity Provider (which have been assigned to the users)

13. Create another Application Role (xsuaaRole2) and map it to AppGrp2 before hitting save.

 

Securing your Java Backend Service

You can prevent unauthorized and/or unauthenticated users from accessing you Java service. It is not necessary to protect a UI5 application if all the data is being fetched from the java service. The UI will hit the java service through approuter itself and if your UI5 application has been configured as one of the destinations in xs-security.json then the authorization token will be forwarded to the UI5 application as well which in turn will be forwarded along with each request that the UI5 application makes to the java service.

The steps are:

  1. Add the following dependencies in your pom.xml
<dependency>
			<groupId>com.sap.xs2.security</groupId>
			<artifactId>security-commons</artifactId>
			<version>0.22.2</version>
		</dependency>
		<dependency>
			<groupId>com.sap.xs2.security</groupId>
			<artifactId>java-container-security</artifactId>
			<version>0.22.2</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-jwt</artifactId>
			<version>1.0.8.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security.oauth</groupId>
			<artifactId>spring-security-oauth2</artifactId>
			<version>2.2.0.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>com.unboundid.components</groupId>
			<artifactId>json</artifactId>
			<version>1.0.0</version>
		</dependency>
		<dependency>
			<groupId>com.sap.security.nw.sso.linuxx86_64.opt</groupId>
			<artifactId>sapjwt.linuxx86_64</artifactId>
			<version>1.0.0</version>
		</dependency>
		<dependency>
			<groupId>com.sap.security.nw.sso.ntamd64.opt</groupId>
			<artifactId>sapjwt.ntamd64</artifactId>
			<version>1.0.0</version>
		</dependency>
		<dependency>
			<groupId>com.sap.security.nw.sso.linuxppc64.opt</groupId>
			<artifactId>sapjwt.linuxppc64</artifactId>
			<version>1.0.0</version>
		</dependency>
		<dependency>
			<groupId>com.sap.security.nw.sso.darwinintel64.opt</groupId>
			<artifactId>sapjwt.darwinintel64</artifactId>
			<version>1.0.0</version>
		</dependency>
  1. Introduce a new class into your project
package com.sap.icd.ref.som.config;

import java.util.Arrays;
import java.util.List;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import com.sap.xs2.security.commons.SAPOfflineTokenServicesCloud;

@Configuration
@EnableWebSecurity
@EnableResourceServer
public class WebSecurityConfig extends ResourceServerConfigurerAdapter {

    private static final String DISPLAY_SCOPE_SOM = "AppRole1";
    private static final String DISPLAY_SCOPE_SOC = "AppRole2";
    public static final String REGEX_TENANT_INDEX = "(!t\\d+)?.";
    private static final String XSAPPNAME = "your xs app name as defined in xs-security.json";
    public static final String DISPLAY_SCOPE = XSAPPNAME + "." + DISPLAY_SCOPE_SOM;
    public static final String UPDATE_SCOPE = XSAPPNAME + "." + DISPLAY_SCOPE_SOC;

    // configure Spring Security, demand authentication and specific scopes

    @Override
    public void configure(HttpSecurity http) throws Exception { //

        String hasScopeSOMAdmin = "#oauth2.hasScopeMatching('" + XSAPPNAME + REGEX_TENANT_INDEX + DISPLAY_SCOPE_SOM
                + "')";

        String hasScopeSOCAdmin = "#oauth2.hasScopeMatching('" + XSAPPNAME + REGEX_TENANT_INDEX + DISPLAY_SCOPE_SOC
                + "')";
	      
	      //to enable cors
        http.cors();

        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER).and().authorizeRequests()
                .antMatchers("POST", "/abc/def").access(hasScopeSOCAdmin)
                .antMatchers("PUT", "/abc/def").access(hasScopeSOCAdmin)

                .antMatchers("POST", "/efg/**").access(hasScopeSOMAdmin)
                .antMatchers("PUT", "/efg/**").access(hasScopeSOMAdmin)
                .antMatchers("GET", "/efg/**").access(hasScopeSOMAdmin)
                .antMatchers("DELETE", "/efg/**").access(hasScopeSOMAdmin)

                

        // http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
    }
		
	  //for cors configuration
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        final CorsConfiguration configuration = new CorsConfiguration();

        List<String> allowedMethods = Arrays.asList("HEAD", "GET", "POST", "PUT", "DELETE", "PATCH");

        // The value of the 'Access-Control-Allow-Origin' header in the response
        // must not be the wildcard '*' when the request's credentials mode is
        // 'include'.
        configuration.addAllowedOrigin("your approuter url");

        configuration.setAllowedMethods(allowedMethods);

        configuration.setAllowCredentials(true);
        configuration.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type"));
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
    // configure offline verification which checks if any provided JWT was
    // properly signed
    
    //this is required
    @Bean
    protected SAPOfflineTokenServicesCloud offlineTokenServices() {
        return new SAPOfflineTokenServicesCloud();
    }
}

3.      In your java service’s manifest file, include the xsuaa service instance name as you mentioned in the approuter.

services:
 - yourpersistenceservice
 - yourxsuaaservicename
  1. Build and Redeploy your java app after adding this code and you should be getting 403 unauthorized when trying to access it directly. The same should work when going through approuter.

 

Changes Required in application UI code:

  1. If you have a UI5 application making ajax calls to your service then use the approuterurl/service/endpoint pattern as the url for your requests.
  2. GET requests will get served without the need of CSRF tokens but POST and PUT requests will require a CSRF token to be sent otherwise you will get a 403 Forbidden.
  3. To get the CSRF token you need to add a header that requests a CSRF token in any of the GET requests to your service. The response header will contain the CSRF token required. Eg:

For persistence of the token you can use this template:

var token = {			
"csrfToken" : ""
};

var oToken = new sap.ui.model.json.JSONModel(token);
sap.ui.getCore().setModel(oToken,'oToken');
var tokenModel=sap.ui.getCore().getModel("oToken").getData();

In any of your GET jquery ajax requests:

headers: {
		ContentType: 'application/json',
		Accept: 'application/json',
		cache: false,
		'X-CSRF-Token': 'Fetch'
         } 

Then retrieve the token from the response:

.done(function(data, textStatus, request) {
		tokenModel["csrfToken"] = request.getResponseHeader('X-Csrf-Token');
		})

Then in any of your POST/PUT requests:

var tokenModel = sap.ui.getCore().getModel("oToken").getData();
	var token = tokenModel["csrfToken"];
      
       $.ajax---
	headers : {'X-CSRF-Token': token}

 

 

We would like to acknowledge the guidance, support and effort put into the research of this topic by Saksham Gupta. He pointed us in the right direction and guided us throughout the research and implementation of the topic. We could not have taken this task to completion without his untiring efforts and research.

To report this post you need to login first.

2 Comments

You must be Logged on to comment or reply to a post.

  1. Matheus Oliveira Goulart

    Hello Bhoomika, nice blog, congrats !!

    See if you can help me, but I’m using on-premise HANA 2.0 (no cloud foundry scenario).

    I’m consuming an external application througth an UI5 app using ajax (rest service). This external service uses Bearer authorization.

    When i deployed the application as stand-alone works perfectly, but i do need to create a site / launchpad for it. When I’m calling this application inside launchap / site, the site router is tring to authenticate the backend Bearer token into HANA (but this token needs to be forwarded to backend system). It returns me the http 401 error (unauthorized – invalid bearer ).

    I’ve already set the xs-app.json AuthenticationMethod as none.

    I did not create a manifest.yaml file referring to the destination and the service xsuaa, could this be the problem?

    Thanks!

     

    (0) 
    1. Saksham Gupta

      Hi Matheus,

       

      You need to have the xsuaa service binding to all the services which are going to interact with your application. I would also recommend you add the destinations and have the forwardAuthToken parameter as true in the manifest.

       

      Please let me know if that works for you 🙂

      (0) 

Leave a Reply