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.
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.