Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
Paul_todd
Product and Topic Expert
Product and Topic Expert
On SAP NEO we have a service you can call to get details about the logged in user. This is called the UserInfo service  (How to get UserName and other login info in SAP UI5 application | SAP Blogs) This was reasonably well documented and worked well on the NEO platform but with the move to Cloud Foundry this no longer works so new techniques are required.




Part of this post is inspired by the sessions I co-wrote for TechEd18 and part by my colleague sachit.aggarwal  who thought it needed a more through treatment!




A bit of background.


I assume you are at least familiar with Cloud Foundry or you will not be here - you know what an Org, account, SubAccount and Space are.


When working within a space in cloud foundry, the user identity is managed by the User Authorization and Access service (UAA) or when using SAP services, the XSUAA. The service will apply the correct oAuth2 flows to enable you to get an oAuth token. However these flows are very complex to implement so SAP have provided another project to do this. This is called the AppRouter that will check to see the incoming request and if it is not authenticated and the request requires authentication then it will log you in to the application by managing the exchange between the browser and the XSUAA service.


Though there are lots of files that need to be created, you should be familiar with them if you have built a project for Cloud Foundry before.


Prerequisites





You need to have:


1. A valid SAP Cloud Platform account (Trial is sufficient) to be able to install and run the code.

2. The Cloud Foundry Command line tools installed. You can download it here





This exercise assumes that you have at least a basic understanding of SAP Cloud Platform and in particular Cloud Foundry. If not there are some great learning journeys on help.sap.com



Start by logging into your cloud foundry account using the command line tooling


cf login


You will now have been logged into an appropriate space in cloud foundry.










Create the xs-security.json file



First we need to create the xs-security file. This file is used to setup the default roles and scopes for the user.


{
 "xsappname": "getuserinfo",
 "tenant-mode": "dedicated",
 "description": "Security profile of getuserinfo",
 "scopes": [
  {
 "name": "uaa.user",
 "description": "UAA"
 }
 ],
 "role-templates": [
  {
  "name": "Token_Exchange",
  "description": "UAA",
  "scope-references": [
  "uaa.user"
  ]
  }
 ]
}







Save the file and call it xs-security.json.


With this file created the instance of the security model can be created


cf create-service xsuaa application node-uaa -c xs-security.json


This completes the configuration of the service.







Create the xs-app.json file



The xs-app file is required to specify the routes for the app router. We could have put this into the approute-start.js file but I wanted to stick to best practices.


 {
 "welcomeFile": "index.html",
  "authenticationMethod": "route",
  "routes": [
  {
  "source": "^/(.*)$",
  "localDir": "resources",
 "authenticationType": "xsuaa"
  }
  ]
}


As you can see this just serves up files in the resources directory which is a standard behaviour for AppRouter. Now it is important to note the authenticationMethod property is set to route, so any accesses are checked to make sure the user is logged in and has a valid access token. If not they will be forced to login.


Also not the authenticatonType property that says this endpoint is protected by xsuaa.


Save the file.


This completes the configuration of the routing.




Create the manifest.yml file


The next file we need is the manifest file. The manifest file is used by the cloud foundry command line to do a build and deploy of the application. This file holds the name, resource requirements and type of application to build as well as the source file.



applications:

- name: getuserinfoapp

instances: 1

memory: 256M

disk_quota: 1024M

path: ./web

buildpack: https://github.com/cloudfoundry/nodejs-buildpack

services:

- node-uaa




Note how the application tells cloud foundry it will consume the node-uaa service.



Save the file.





This completes the configuration of the deployment descriptor.


Create the approuter-start.js file



The most important feature of this post is the file that configures app router to support our new URL to obtain the email address of the logged in user.


const approuter = require('@sap/approuter');
var ar = approuter();
ar.beforeRequestHandler.use('/getuserinfo', function (req, res, next) {
  if (!req.user) {
  res.statusCode = 403;
  res.end(`Missing JWT Token`);
  } else {
  res.statusCode = 200;
  res.end(`My name is ${JSON.stringify(req.user.name, null, 2)}`);
 }
});
ar.start();



Save the file.


The approuter extension is now complete.



Create the package.json file


Since this is a node.js project we need a package file to enumerate the dependencies


{
 "name": "getuserinfo",
 "version": "0.0.1",
 "description": "Service to get user info",
 "dependencies": {
    "@sap/approuter": "5.8.0"
 },
 "scripts": {
 "start": "node ./approuter-start.js"
 }
}



This is very simple as we just have a dependency to the app router. Note however the start property in the scripts section. This property will, instead of launching the default app router will actually launch a javascript file that will start the app router. This file will allow us to create the app router with a custom configuration.


Save the file as package.json


The package description is now complete.



Create the index.html file


We create a simple HTML file to show the system is working. This is as basic an HTML file as possible

<!doctype html>

<html>

  <body>

      <div>

           Welcome to user info

      </div>

  </body>

</html>


Save the file as index.html into the resource folder, creating the resources folder if it is not present.

The completes the configuration of the HTML file.


 

Putting it all together


Now all the files have been created, the application can be deployed to cloud foundry using the command line tooling.


The command we want to use is:


cf push


This takes the manifest.yml file and deploys the project as a node.js project. The package file contains a list of the dependencies as well as the start function to be called when the deployment is finished.


How it works








The browser will connect to the deployed application and check the user is logged in. If they are not then AppRouter implements the oAuth flow to log you in and get an oAuth token.


Additionally a JWT token will also be generated and this holds additional information about your session.



When a request arrives, the xssec (passport) library will check for the token and if not present execute the auth flow to get the JWT and oAuth token. If it is present it will parse the JWT and add the fields to the incoming request object to make it easier to consume the token in code.


Thoughtfully the app router annotates the req object with the relevant information by using the passport library together with the xssec library. A full list of node js packages can be found on help.sap.com



When the /getuserinfo path is called, the user info attached to the request (which should always be present as we have said all routes need a valid token) is read and the email address extracted and returned to the caller.






Wrapup





So folks, that is how you get the email address of the logged in user when in Cloud Foundry. More complex than NEO but more flexibility as well.



11 Comments
miltonc
Product and Topic Expert
Product and Topic Expert
Awesome Paul.  I was able to follow the tutorial and can get the email address of the logged in user.  But the req.user object doesn't have information like firstname, lastname, user id etc.  Anyway, we can get that info as well...
0 Kudos
Hi,

 

Did you find any way out to get the information you required?
0 Kudos
Hi Milton ,

May I know how you are able to get the email address . What url you are using to call the getUserinfo .
former_member662960
Participant
0 Kudos
Hi Milton,

 

I'm not able to get the firstname, lastname and user id you mentioned. They are neither found in the req.user, or in the req.session.user.

Do you know where I can find them? 🙂
WFlats
Participant
0 Kudos
Hi,

I added this code to an existing app on Web IDE.

But I'm also struggling with the URL.

Whatever I try (e.g. "...-approuter.cfapps.eu10.hana.ondemand.com/getuserinfo" or adding my app name and version) I receive a "Not found" error.

I have the impression the approuter-start.js is not executed. But I included

"scripts": {
"start": "node ./approuter-start.js"
}

in my package.json and the files are next to each other.

Has anyone an idea what's wrong or sample code or a hint how I could find out?
former_member275826
Participant
0 Kudos
Hi Wolfgand,

 

I am also facing same issue.

 

Have you got working?

 

 
RobertO
Advisor
Advisor

You can get logged user (first name, last name, email)  information’s on Cloud Foundry out of the authentication service with reading JWT token. This works if standard SAP IdP provider is used. In Postman you can get this information calling API with GET operation https://<account&gt;.authentication.eu10.hana.ondemand.com/userinfo

https://docs.cloudfoundry.org/api/uaa/version/74.20.0/index.html#user-info

Before making call you have to get Bearer token which can be retrieved by calling POST operation https://<account&gt;.authentication.eu10.hana.ondemand.com/oauth/token

If you access from your application in CF where you are already logged in then you should use destination with following settings:

Name : xsuaa_api

Type: HTTP

URL : https://<account&gt;.authentication.eu10.hana.ondemand.com/

Proxy type : Internet

Authentication : OAuth2UserTokenExchange

Client id : from XSUAA service instance

client secret : from XSUAA service instance

Token service URL : https://<account&gt;.authentication.eu10.hana.ondemand.com/oauth/token

Destination can be consumed out of UI5 app with .ajax GET call or out of any other service. More on how to consume destination service :

https://help.sap.com/viewer/cca91383641e40ffbe03bdc78f00f681/Cloud/en-US/7e306250e08340f89d6c103e288...

 

gauravkumarm
Discoverer
0 Kudos
Hi Robert,

 

We have recently moved to SAP cloud foundry. We have suite of apps which require the user api.

As you have mentioned, we can use the user api via destination. I followed the steps and tried to create a destination to utilize the user api service, but was unable to do so.

I created the xsuaa instance with all the available plans but each time the authentication method populated was different. I changed it to OAuth2UserTokenExchange and saved it, but it gave error. I went with the populated authentication method as well but again the result was same.

It would be great if you guide us on this, or provide the steps to achieve the same.

Regards,

Gaurav
arjun_thakur
Active Contributor
0 Kudos
Hi Robert,

I tried following the solution that you have suggested, but i am unable to get the logged in user info by calling the following API:

https://<account&gt;.authentication.eu10.hana.ondemand.com/userinfo

I created an instance of XSUAA service, i called the following service in postman to get the access token:

https://<account&gt;.authentication.eu10.hana.ondemand.com/oauth/token

When i call the "userinfo" API using the access token, i get the following error:

"The subdomain does not map to a valid identity zone"

Can you please advise how to resolve this.

Regards,

Arjun
rronald
Explorer
0 Kudos
Hi Pravin

You managed to solve this issue, since I am trying to do this myself, but the same thing happens to me.

 

 
sweetysingh
Participant
0 Kudos
Hi paul.todd1,

Great that it worked for many but unfortunately, I am unable to follow the blog and could not get email id.

Could you or someone please share GitHub complete code?

Thanks!