Skip to Content
Technical Articles

SAP Cloud Platform Backend service: Tutorial [25]: Understanding: App Router (2)

Quicklinks:
Chapters
Sample project files

Next round of our Application Router learning.

In the first part, we’ve created our first App Router configuration and defined a route from the root URL to the SAP homepage.
So, when our app in Cloud Foundry is opened, the user is always directly redirected to that homepage.

Today, we’re going to extend the configuration: enabling authentication, and others

Everything is done without one single line of code

Overview

1 Very simple route (previous blog)
2 Different route
3 Add static resource
4 Customize static resource
5 Add authentication
6 Add authorization
7 Configure authorization
8 Route to Backend service without additional login (next blog)
9 Calling Backend service via App Router from external REST client tool (next blog)

Prerequisite

You should have gone through Part 1

Preparation

You can reuse the existing app from previous tutorial, or you can create the same project structure again, then deploy with different name

Learning 2: try different route

Compared to the previous learning, we’d like to allow fine-grained routing.
So instead of offering only the app-root-URL to the user, we want to offer multiple entry-URLs.
We can define multiple routes in xs-app.json
Depending on the URL which is opened by the user, we route to different target.
For example:
/approuter/take-me-to-Joe
would redirect to Joe
And
/approuter/take-me-to-Jim
would redirect to Jim

This is realized via the “source” property.
To keep the chapter short, we just show how to define such a route.
(You can of course add more routes and destinations to your playground-project)
xs-app.json:

{
  "authenticationMethod": "none",
  "routes": [
    {
      "source": "^/take-me/to-sap/(.*)$",
      "target": "$1",
      "destination": "env_destination_saphome"
    }
  ]
}

After deploy, first try to open the root URL, like in previous blog:

https://myapprouterconfapp.cfapps.eu10.hana.ondemand.com/

Result:
It doesn’t work, the App Router doesn’t find a match for “/”
The try the correct URL:

https://myapprouterconfapp.cfapps.eu10.hana.ondemand.com/take-me/to-sap/

Result:
The SAP homepage is displayed, as expected

Note:
Make sure to clear the browser cache when changing routes.
Otherwise the browser might still serve the cached URLs
You might as well delete your deployed app before re-deploying it

Note
Don’t forget the slash at the end of the URL

Learning 3: Add static resource

A “static resource” can be a web page, e.g. a homepage
Let’s try it.

Add default resources folder
C:\tmp_approuter\appfolder\resources
To add a default welcome file, step into that folder and create a file index.html
Paste the following content:

<html>
  <body>
    <h1>Index file of AppRouter Configuration app </h1>
    <a href="/take-me/to-sap/">Go to SAP</a>
  </body>
</html>

Explanation
When the root URL of the App Router is invoked, the App Router checks if there’s a default welcome-file (index.html) in the default folder (/resources).
If yes, then it is opened.
So, that’s why we’ve done the above steps?
Yes
And it works?
Try it
Why that strange hyperlink in above html?
Ah, I’ve forgotten to explain (or somebody has distracted me)
I’m your alter ego, I cannot distract
OK, we can see that the link is a relative URI, relative to the root of the application,
And we can see that the relative URL is the same like a route which we defined in the xs-app.json file above
So the final full URL is the same like above
And it works?
Try it
How?
After deploy, open the root URL of the app:
https://myapprouterconfapp.cfapps.eu10.hana.ondemand.com/

Result:

Cool – and the hyperlink?
Try it

Learning 4: customize static resources

To turn the implicit default behavior into explicit configuration, let’s make things visible by quickly adding our customization:
Rename the index.html file to AppRouterHome.html
Change some words of the text.
Add the following line to your xs-app.json

{
  "welcomeFile": "AppRouterHome.html",
  "authenticationMethod": "none",
  "routes": [
  . . .

After deploy, open the root URL
https://myapprouterconfapp.cfapps.eu10.hana.ondemand.com
And you’ll witness the redirect: the App Router opens
https://myapprouterconfapp.cfapps.eu10.hana.ondemand.com/AppRouterHome.html

Learning 5: add authentication restriction

It’s time to take a serious step forward: We want to protect our app
Why? It doesn’t do anything serious…?
Don’t ask
The good news is: we don’t need to do anything ourselves.
We only need to do some configuration:

1. Create instance of XSUAA
2. Bind our app to that instance
3. Declare authentication in xs-app.json

1. Create instance of XSUAA

You promised that we don’t need to do anything
That’s still true: the authentication is done by App Router and XSUAA
We only need to create an instance of the Authentication service offered by the SAP Cloud Platform Cloud Foundry Environment
So we do have to do something
Of course.
But if you already have an instance, you can probably just use it, no special configuration is required (for the moment)

To create a service instance, with name “XsuaaForApprouterConf”, you can use the following command (see here for info):

cf create-service xsuaa application XsuaaForApprouterConf -c “{ \”xsappname\”:\”XsuaaForApprouterConf\”,\”tenant-mode\”:\”dedicated\” }”

Or use the Cockpit and add these parameters in the creation wizard:

{
  "xsappname": "XsuaaForApprouterConf",
  "tenant-mode": "dedicated"

}

2. Bind our app to XSUAA

The new instance of XSUAA service doesn’t do anything if we don’t bind it.
As such, we add the following 2 lines to our manifest.yml file:

  services:  
  - XsuaaForApprouterConf

Note:
Be careful when pasting the lines, remember the format is yaml
Yes, I know it is very picky…

Note:
The service instance and the binding is only required, if we want to force authentication for the usage of our App Router Config App
That’s why we didn’t need any XSUAA until now

See the appendix for full manifest content

3. Declare authentication in xs-app.json

The xs-app.json allows to fine-tune the authentication configuration

Until now, we’ve used the top-level property

“authenticationMethod”: “none”

Now we set it to “route”:

“authenticationMethod”: “route”

This means that authentication is enabled and is delegated to the routes
Which allows each route to independently control the authentication
Means, one route can require authentication, others not
As per default, the route-authentication is enabled
But we still do set it, to make it explicit (for the learning experience)
The property in the route section is called “authenticationType”, value can be none or xsuaa
The value xsuaa means that authentication is handled by XSUAA service instance, that’s of course what we want

“authenticationType”: “xsuaa”,

Note:
The App Router is not used to “protect” the destination URLs.
They remain visible, if not themselves protected in the application code
In our example, obviously, sap.com is still accessible
Pouuhhhh, I was already concerned

See the full xs-app.json

{
  "authenticationMethod": "route",
  "welcomeFile": "AppRouterHome.html",
  "routes": [
    {
      "authenticationType": "xsuaa",
      "source": "^/take-me/to-sap/(.*)$",
      "target": "$1",
      "destination": "env_destination_saphome"
    }
  ]
}

Try it

Open Browser window and also dev tools (network monitor tab)
Type the root URL into the browser window
myapprouterconfapp.cfapps.eu10.hana.ondemand.com
Result: the user login page:

Check the dev tools:

We can see that the App Router has implemented the OAuth flow with grant type “Authorization Code”:
App Router has redirected to the “authorize” endpoint of the OAuth Authorization Server (which is the XSUAA)
The Request URL contains the parameters as specified by OAuth flow
Furthermore, we can see that App Router implementation offers an endpoint for the redirect of the Authorization Server.
Nice, my app has a login/callback URL, which I even didn’t know

OK, back to the browser, we enter the user credentials of e.g. Trial Account user
Result: our homepage of our App Router Config App is displayed
The dev tools:

We can see that the XSUAA-Authorization Server has called the callback-URL or our App Router and has sent the “Authorization Code”
The “Location” header points to our homepage

Advertisement:
I would like to take this opportunity to redirect your attention to a couple of interesting blogs
They will ultimately clarify any questions and doubts about OAuth flow
You don’t need to be very interested in security, it is easy learning stuff, as usual:
Understanding the OAuth flow when calling APIs from external app
Understanding the OAuth flow with grant type “Authorization Code”

Learning 6: add authorization restriction

You know that authentication is about allowing access only for registered users
But authorization is about checking if the user has a certain role

Example:
A web app has a web page used for administrative tasks (e.g. maintain database)
Obviously, not everybody is allowed to use it.
As such, a special user can have the “Admin” role, and only that user would be allowed to access it.

Can I get the role for modifying your silly blogs?
Other very special users can have the role of getting on my nerves
Oh, cool, I like this role

In our App Router configuration, we add one more property: the “scope”
As value, usually an existing role must be entered.
But for our first silly example, it is enough to just enter a fantasy role
Can I suggest “dragonHunter” ?
No.
Please use the following config:

{
  "welcomeFile": "AppRouterHome.html",
  "authenticationMethod": "route",
  "routes": [
    {
      "authenticationType": "xsuaa",
      "scope": [ "$XSAPPNAME.homepageAccessScope" ],
      "source": "^/take-me/to-sap/(.*)$",
      "target": "$1",
      "destination": "env_destination_saphome"
    }
  ]
}

We can see that the route requires a scope which doesn’t exist in our identity zone (subaccount).
After deploy, we open our homepage, click the hyperlink.
The login-screen is presented
There, we enter our valid user credentials
Afterwards, the result is:
“Forbidden”
That’s expected, because our user doesn’t have such role
I hate forbidden
OK, calm down and continue reading

Learning 7: assign required authorization to our user

This learning is optional
Aha: because the others were so mandatory?
This learning is even more optional
Is it  interesting?
No but you’ll get into the “forbidden” space…
Great, THAT’S what I want
OK, but you need patience
I don’t like patience
sigh…

What we learn in this chapter is nothing than configure the security setup, with the goal of enabling our user to pass the authorization check of our App Router configuration.

Remember:
1 Our first restriction was to add authentication check
The check is done by the App Router and XSUAA
-> we could pass this hurdle by entering user credentials of our Trial account user
This user does obviously exist in the Identity provider which is assigned to our Trial Account
As such, we didn’t need to create a user etc

2 Second restriction: add authorization check
This check is also done by the App Router and XSUAA
-> to overcome this hurdle we need to do 2 steps:
1 Create the scope-definition in XSUAA
2 Assign the corresponding role to our user

Let’s have a closer look at the restriction

“scope”: [ “$XSAPPNAME.homepageAccessScope” ]

This restriction says: the user who wants to go through this route must have the role called “$XSAPPNAME.homepage”

Now, how does the App Router check it?
It does it with the help of XSUAA, the central User Account and Authorization instance in the  Cloud Foundry Environment
Remember:
We have to bind our App Router app to an instance of XSUAA if we need authentication and authorization
XSUAA has the knowledge of the existing scopes
As such, the scope “$XSAPPNAME.homepage” must be defined in the instance of XSUAA to which we bind our app

1 Define the scope in XSUAA

In learning 5 we’ve already created an instance of XSUAA, but this one is not sufficient, it doesn’t contain the scope
As such, let’s delete that instance and create a new one

To delete, you can use this delete command: cf ds XsuaaForApprouterConf -f
or use the cockpit

Now create a new instance. It should contain the scope-definition in the JSON-parameters.
This time, let’s try a different alternative for passing the JSON parameters to the new service instance:
We write the JSON-parameters into a json-file

In your file system, go to the root directory of the app: C:\tmp_approuter
Create a file with name xs-security.json
Paste the following content:

{
  "xsappname": "XsuaaForApprouterWithScope",
  "tenant-mode": "dedicated",
  "scopes": [
    {
      "name": "$XSAPPNAME.homepageAccessScope",
      "description": "Allows access to the homepage"
    }
  ],
  "role-templates": [
    {
      "name": "RTHomepageAccess",
      "description": "Role Template for accessing the homepage",
      "scope-references": [
        "$XSAPPNAME.homepageAccessScope"
      ]
    }
  ]
}

Explanation:

We define a scope “$XSAPPNAME.homepageAccessScope”
The operation covered by that scope: to access the homepage
The scope is wrapped in a “role-template” (it can wrap multiple fine-granular scopes)
The “role-template” is what we will see later in the cloud cockpit

The parameters for creating the service instance are passed as file-reference (not inline)
To create the service instance in the cockpit, in the creation-wizard browse to the xs-security.json file, then give a name for the instance as “XsuaaForApprouterWithScope”
Or use this command:
cf create-service xsuaa application XsuaaForApprouterWithScope -c xs-security.json

After creation of the service instance, we need to bind it to the app
As such, we have to edit the manifest.yml:
We delete the previous binding and replace it with the name of the new service instance: “XsuaaForApprouterWithScope”

services:
– XsuaaForApprouterWithScope

To be on the safe side, we can check that the restriction in the route has exactly the same name:

“routes”: [
{
“authenticationType”: “xsuaa”
“scope”: [ “$XSAPPNAME.homepageAccessScope” ],

Finally, let’s delete our app in cloud foundry (again to be on the safe side):
cf d myApprouterConfigurationApp -r -f

And deploy it from scratch

After deploy, we can check the environment variables to see the correct assignment to the new service instance:
cf env myApprouterConfigurationApp

2. Assign the required role to our user

At this point in time, we have defined a scope, and used it as restriction
We can try to open our App Router Config App and access the route, but it will be rejected
I don’t believe
Try it, the app is deployed and I give you the link: https://myapprouterconfapp.cfapps.eu10.hana.ondemand.com/take-me/to-sap/
Hey, it doesn’t work!!!
Yes, it was expected
So why have I done it, if it was expected to fail???
Sigh
And why it doesn’t work?
Because our user doesn’t have the required role
Which role?
The role which is required to pass the route-restriction: the homepage access role
I want this role !!
Yes, then just keep quiet and do what I tell you

Login to the SAP Cloud Platform and go to your Cloud Foundry Trial account, then to your subaccount (that one to which you deployed the app etc)
On subaccount level, expand the “Security” menu entry on the left navigation pane
Click on “Role Collections”
Then the button “New Role Collection”
Enter a name of your choice, e.g. “RC_HomepageAccess”

The new role collection is added to the list
Until now, the collection is empty.
So click it to go to the Configuration screen of the new role collection

Now let’s add a role
Press “Add Role”
In the dropdown, choose the XSUAA instance we created above: “XsuaaForApprouterWithScope”
Afterwards, the dialog displays the role template which we defined in the XSUAA parameters (only one role)

Press Save

Now we have a role collection which contains the role which is required for accessing the homepage
Cool, can I do it now?
No no, there’s still one thing missing
A good blog?
No, the role collection is useless if it is not used
Ah
We have to assign it to our user
Can’t we assign directly the role to our user?
That’s not possible.
Why
Roles are too small, too fine granular
Furthermore, even a role collection cannot be assigned directly to a user
Why?
Because our user is maintained in an identity provider.
And?
IDPs don’t know about Cloud-Foundry role collections
Internally, they deal with groups”
A group is mapped to a role collection
Can we stop talking and do it?
Yep

Go back to the subaccount overview page and again expand the “Security” menu entry
Click on “Trust Configuration”
Click on “SAP ID Service” (the default identity provider in Trial account)
Enter the E-Mail Address of your user and press “Show Assignments”
Then press “Assign Role Collection”
In the dialog, choose the role collection created above
Then press “Assign Role Collection”

Can I?
Yep, now it should work
Open the URL in browser
https://myapprouterconfapp.cfapps.eu10.hana.ondemand.com/take-me/to-sap/
After entering the user credentials, we can see the homepage
Yes it works, I’m innnnnnnn…… Wow, that feels goooooooood…
Good that it is so easy to make you happy….
sigh…

Little recap before summary
In the past 2 learning chapters, we’ve learned how to configure our route to require authorization.
Furthermore, we’ve learned how to configure our user to overcome that restriction
All in all, the required steps for authorization in App Router:

1 create XSUAA instance with a scope and role-template (can be written in xs-security.json)
2 bind the app to that service instance (manifest.yml)
3 declare a scope-restriction in the route (xs-app.json)
4 in cockpit, create role collection and assign the role (which is specified in step 1)
5 in cockpit, assign the role collection to the user (Trust Configuration)

Summary

In the previous tutorial we’ve learned the first steps to configure and deploy an App Router in Cloud Foundry
In this tutorial we’ve learned a little bit more about what can be done in the App Router configuration:
Configuring “source” URL pattern
Adding static resources
Adding authentication restriction
Adding authorization restriction
Configuring access with required authorization

In the next tutorial we’ll learn a real use case: accessing Backend service API through App Router

Don’t miss it !!!

Looking forward!
What? A friendly comment? How unusual…
Anyways, I won’t read the tutorial
sigh…

Links

SAP Help Portal: Application Router documentation entry page
Overview of tutorial series

Appendix: Sample project files

See here exemplary files, we cannot add sample projects for each chapter

xs-security.json

{
  "xsappname": "XsuaaForApprouterWithScope",
  "tenant-mode": "dedicated",
  "scopes": [
    {
      "name": "$XSAPPNAME.homepageAccessScope",
      "description": "Allows access to the homepage"
    }
  ],
  "role-templates": [
    {
      "name": "RTHomepageAccess",
      "description": "Role Template for accessing the homepage",
      "scope-references": [
        "$XSAPPNAME.homepageAccessScope"
      ]
    }
  ]
}

manifest.yml

---
applications:
- name: myApprouterConfigurationApp
  host: myapprouterconfapp
  path: appfolder
  memory: 128M
  services:
  - XsuaaForApprouterWithScope
#  - XsuaaForApprouterConf  # this binding was used in previous learning chapter
  env:
    destinations: >
      [
          {
              "name": "env_destination_saphome",
              "url": "http://sap.com/"
          }
      ]

xs-app.json

{
  "welcomeFile": "AppRouterHome.html",
  "authenticationMethod": "route",
  "routes": [
    {
      "authenticationType": "xsuaa",
      "scope": [ "$XSAPPNAME.homepageAccessScope" ],
      "source": "^/take-me/to-sap/(.*)$",
      "target": "$1",
      "destination": "env_destination_saphome"
    }

  ]
}

package.json

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

AppRouterHome.html

<html>
  <body>
    <h1>Homepage of AppRouter Configuration app </h1>
    <a href="/take-me/to-sap/">Go to SAP</a>
  </body>
</html>
Be the first to leave a comment
You must be Logged on to comment or reply to a post.