SAP XSUAA API to get all scopes of a user in subdomain

Developing application on cloud platforms includes user authentication and authorization management. SAP Cloud platform provides SAP XSUAA (Extended Services for User Account and Authentication) which is an extension of cloud foundry UAA. SAP XSUAA is a component which communicates between Identity provider and the application running on the cloud platform.

This Blog post describes how to use XSUAA to know more about the logged-in user details using JSON Web Token (JWT) received by the application and also using XSUAA API. This Blog post guides step by step in achieving the goal in a JAVA Web application.

Summary: Building a JAVA web application which extracts logged-in user’s information using XSUAA.

Step 1: Creating a JAVA Web Application

This step describes about creating a JAVA Web Application using Maven.

In the development IDE which has Maven configured, create a new maven project by selecting maven-archetype-webapp as archetype. In eclipse the project structure looks as below,

Build the Web Application by running “mvn clean install” under the project folder (where pom.xml resides)

Step 2: Create a manifest file to deploy the Web Application

Manifest file is the deployment descriptor which contains information about Application name, path of the war file to be deployed, memory required, build_pack required, consuming services and environment variables if required.

Create a file named manifest.yml under project folder and paste below content into the file.

--- applications: - name: xsuaa-sample memory: 1G path: target/ buildpack: sap_java_buildpack env: TARGET_RUNTIME: tomee JBP_CONFIG_DEBUG: '{enabled: true}' SAP_JWT_TRUST_ACL: "[{\"clientid\":\"*\",\"identityzone\":\"*\"}]"

Step 3: Deploy the Application on SAP Cloud platform

SAP Cloud Platform account and space in cloud foundry environment and cloud foundry CLI are pre-requisites for the deployment. Deployment of web application can be done by running cf push under the project folder where manifest.yml file exists.

cf push

Deployed web application can be now seen in the SAP Cloud Platform cockpit.

Now let’s launch the application by clicking on the url under Application Routes, which shows the index page of the web application as below.

Till now JAVA web application is created and deployed without any security constraints and user authentication and authorization configurations.

Step 4: Protect Web Application with a role

For protecting web application with a user role, add below mentioned configuration under <web-app> tag in the web.xml file in the web application project.

 <security-role> <description>Read permission for the user</description> <role-name>Hello.User</role-name> </security-role> <security-constraint> <display-name>Hello User Access</display-name> <web-resource-collection> <web-resource-name>All resources protected</web-resource-name> <url-pattern>/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>Hello.User</role-name> </auth-constraint> </security-constraint>

Build the application using mvn clean install and re-deploy the application to cloud platform by running cf push.

Now application shows forbidden error when application URL is opened in the browser. This means the user doesn’t have appropriate permissions to access the application.

Step 5: Create XSUAA instance

Since the web application is protected with Hello.User role, this role has to be created in the platform so this role can be assigned to the users. For doing that we have to create an XSUAA instance with proper configurations.

Here the target is to build single-tenant application, so i’m using application plan to create XSUAA instance with the below configurations. This XSUAA instance has to be binded with web application and approuter (which we create in next step). The XSUAA instance name provided for this exercise is “xsuaa-sample

{ "xsappname": "xsuaa-sample", "tenant-mode": "dedicated", "authorities": [ "$XSAPPNAME.Hello.User" ], "scopes": [ { "name": "$XSAPPNAME.Hello.User", "description": "Permission for accessing xsuaa-sample app" } ], "role-templates": [ { "name": "HelloUser", "description": "Permission required to access xsuaa-sample web app", "scope-references": [ "$XSAPPNAME.Hello.User" ] } ], "role-collections": [ { "name": "Hello.Users", "description": "Permission required for xsuaa-sample app", "role-template-references": [ "$XSAPPNAME.HelloUser" ] } ] }

Using this Hello.Users role collection will be created, which in-turn refers to HelloUser role template and Hello.User scope, which is what our web application is also protected with.

Step 6: Create an Application Router

Application Router (approuter) is used to connect web application to the centrally provided User Account and Authentication (UAA) service or XSUAA.

Approuter is a node.js project and contains a package.json and xs-app.json defining the routes and also contains logout html page. The project structure is shown below.

Contents of package.josn is as below.

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

Contents of xs-app.json is as below.

{ "authenticationMethod": "route", "routes": [ { "source": "^/logout-page.html$", "localDir": "my-static-resources", "authenticationType": "none" }, { "source": "^/(.*)", "destination": "xsuaa-sample-webapp", "authenticationType": "xsuaa", "csrfProtection": false } ], "logout": { "logoutEndpoint": "/logout", "logoutPage": "/logout-page.html" } }

The approuter deployment descriptor (manifest.yml) looks as below, which specifies buildpack as nodejs_buildpack and a destination which points to our web application URL. The destination name should match the destination value provided in the xs-app.json. Also specifying our XSUAA instance name under services.

--- applications: - name: xsuaa-sample-approuter path: xsuaa-sample-approuter memory: 128M buildpack: nodejs_buildpack env: destinations: > [{ "name": "xsuaa-sample-webapp", "url": "", "forwardAuthToken": true }] services: - xsuaa-sample

Deploy the approuter by executing “cf push” under the folder where the manifest.yml of approuter exists.

Step 7: Bind the XSUAA instance with Web Application

Approuter is already binded to XSUAA instance, now to bind the web application, go to cockpit -> Applications -> web application -> Service Bindings.
Click on the Bind Service button and select service from catalog, select account and authorization service and select re-use already created instance and select xsuaa-sample instance.

Also to make sure that our web application understands the XSUAA, login-config has to be added in web.xml of the web application as below under the <web-app> tag.

 <login-config> <auth-method>XSUAA</auth-method> </login-config>

Re-build the web application by running mvn clean install and re-deploy to the cloud platform by running cf push.

Step 8: Assign Required Role

As the web application is protected with Hello.User scope and this scope is packaged in Hello.Users role collection (in step 5), this role collection needs to assigned to the user to provide access to web application. Role collection assignment can be done using below steps,

Got to Trust Configuration

Click on sap.default Identity provider which opens role collection assignment page as below,

Enter email address of the user, whom role collection has to be assigned.

Click on the Show Assignment button, then click on Assign Role collection, which open a dialog showing list of role collections, select Hello.Users and click on Assign Role Collection button.

Now open the approuter url in the browser and login in with email and password.

Step 8: Add required dependencies to web application pom

Before implementeing the servlet let’s add the required dependencies to the web application pom.xml. Below are the list of dependencies need to be added and reason for their addition is specified in the comment for each one.

 <!-- for java servlet implementation --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.0.1</version> <scope>provided</scope> </dependency> <!-- xsuaa library --> <dependency> <groupId></groupId> <artifactId>api</artifactId> <version>2.6.2</version> <scope>provided</scope> </dependency> <!-- Http Client library for calling xsuaa rest api --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.8</version> </dependency> <!-- for json parsing --> <dependency> <groupId></groupId> <artifactId>gson</artifactId> <version>2.8.5</version> </dependency> <!-- for reading http response stream --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.6</version> </dependency>

Step 9: Create a java POJO for servlet response structure

The servlet which will be implemented provides below informatin about the logged-in user,

  1. Role collections assigned to the user
  2. All scopes of the logged-in user in the current subdomain

So, let’s create a java POJO which describes the servlet response structure.
Create a package under src/main/java with name ‘‘ and create a java class ‘User’. Add below code to the file.

package; import java.util.List; public class User { private List<String> roleCollections; private List<Scope> allScopesInSubDomain; public List<String> getRoleCollections() { return roleCollections; } public void setRoleCollections(List<String> roleCollections) { this.roleCollections = roleCollections; } public List<Scope> getAllScopesInSubDomain() { return allScopesInSubDomain; } public void setAllScopesInSubDomain(List<Scope> allScopesInSubDomain) { this.allScopesInSubDomain = allScopesInSubDomain; } public class Scope { private String value; private String display; private String type; public String getValue() { return value; } public void setValue(String value) { this.value = value; } public String getDisplay() { return display; } public void setDisplay(String display) { this.display = display; } public String getType() { return type; } public void setType(String type) { this.type = type; } } } 

Step 10: Create a Servlet in the java web application

Create a Servlet class ‘UserInfo’ in java web application and add below contents to the class, where GET method is implemented to fetch all role collections of the logged-in user from the JWT and all scopes of the logged-in user in the current subdomain using XSUAA ‘/Users’ REST API.

package; import; import; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import; import org.apache.http.HttpStatus; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import; import; import; import; import; import; import; import; /** * Servlet implementation class UserInfo */ public class UserInfo extends HttpServlet { private static final long serialVersionUID = 1L; /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse * response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("application/json"); User user = new User(); XSUserInfo userInfo = (XSUserInfo) request.getUserPrincipal(); // removing Bearer String jwt = userInfo.getAppToken(); try { // fetch all role collections from JWT of the logged-in user String[] roleCollections = userInfo.getSystemAttribute("xs.rolecollections"); user.setRoleCollections(Arrays.asList(roleCollections)); } catch (XSUserInfoException e) { System.out.println("Error : " + e.getMessage()); } String userId = null; String subDomainAuthUrl = null; String subDomainUserInfo = null; JsonParser parser = new JsonParser(); if (jwt == null) { response.setStatus(HttpStatus.SC_FORBIDDEN); response.getWriter().append("No JWT received"); } String[] jwtParts = jwt.split("\\."); String jwtDecoded = new String(Base64.getDecoder().decode(jwtParts[1])); JsonElement eleJWT = parser.parse(jwtDecoded); // Fetching userid from JWT if (eleJWT != null) { JsonObject objJWT = eleJWT.getAsJsonObject(); userId = objJWT.get("user_id") != null ? objJWT.get("user_id").getAsString() : null; } // fetch sub domain authentication url String JWTMetaDataDecoded = new String(Base64.getDecoder().decode(jwtParts[0])); JsonElement eleJWTMetadata = parser.parse(JWTMetaDataDecoded); if (eleJWTMetadata != null) { JsonObject objMetadata = eleJWTMetadata.getAsJsonObject(); subDomainAuthUrl = objMetadata.get("jku") != null ? objMetadata.get("jku").getAsString().split("/token_keys")[0] : null; } if (userId != null && subDomainAuthUrl != null) { CloseableHttpClient httpClient = HttpClients.createDefault(); HttpGet get = new HttpGet(subDomainAuthUrl + "/Users/" + userId); get.setHeader("Authorization", "Bearer " + jwt); CloseableHttpResponse xsuaaResponse = httpClient.execute(get); if (xsuaaResponse.getEntity() != null && xsuaaResponse.getEntity().getContent() != null) { InputStream ins = xsuaaResponse.getEntity().getContent(); subDomainUserInfo = IOUtils.toString(ins); } } // Parsing XSUAA auth API response for all scopes (groups) if (subDomainUserInfo != null) { JsonElement subDomainUser = parser.parse(subDomainUserInfo); if (subDomainUser != null) { JsonArray groupsArr = subDomainUser.getAsJsonObject().getAsJsonArray("groups"); List<User.Scope> scopes = new ArrayList<User.Scope>(); for (int i = 0; i < groupsArr.size(); i++) { User.Scope scope = new Gson().fromJson(groupsArr.get(i), User.Scope.class); scopes.add(scope); } user.setAllScopesInSubDomain(scopes); } } response.getWriter().append(new Gson().toJson(user).toString()); } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse * response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub doGet(request, response); } } 

Step 11: Build, Deploy and Test

Build java web application and re-deploy on SAP Cloud Platform.

Now open the below url in the browser which hits our servlet, login in using the email and password. Then you should be able to view the role collections and all scopes of the logged-in user.

Response will be similar to below json,

Thank you, Hope this is helpful.

Please comment your questions and feedback.