Hands-On Tutorials
Developing Secure Applications on the SAP Cloud Platform
In this blog series, we explore authentication and authorisation using XSUAA in the SAP Cloud Platform, Cloud Foundry environment.
In this blog, we cover how we can perform authorization checks in our business logic. For this we will
- Define scopes and role templates in the security descriptor file for the XSUAA service | xs-security.json.
- Add the body-parser package to the approuter to parse JSON from HTTP POST.
- Add XSUAA Container Security API methods to our Business Logic App to perform authorization checks.
- Create a role collection adding roles and adding user(s) using SAP Cloud Platform cockpit.
Video Tutorial
In this seventh video of the series, we explore authorizarion using XSUAA service with some code changes to the business logic of our services app and to the front-end, the app router.
https://youtu.be/hfKkclnhVHQ
Sample Code
As before, we continue with the (slightly modified) sample code from SAP Cloud Platform documentation:
You can download the sample code from repository
Appendix
For more detailed information about the SAP Cloud Platform trial environment, Cloud Foundry buildpacks, dependencies declarations, attributes, Diego cells and more, see the "appendix" blog
Application Router
We continue with the approuter as configured in the previous blog where we installed the
@sap/approuter package and defined the route(s) in the xs-app.json file.
User Interface | Index.html
So far we have used a very simple home page (index.html) for the web entry point (approuter) of our business logic application.
In this example, we are going to do something a little bit more elaborate: view and update users.
The body of the page contains a link to trigger a GET request: /myapp/users (same as before) and a button and input box for a POST request.
The JavaScript required to support this is defined in the header. We need to fetch a CSRF token first before sending the POST. This is why the
addUser function calls
addNewUser inside f
etchCsrfToken and we need a bit more code then before on our home page.
See
learn.jquery.com/ajax for more information about jQuery and AJAX. For the token, see
cross-site request forgery.
XSUAA Service Instance
Security Descriptor | xs-security.json
When we created an XSUAA service instance previously we only provided the name of the business logic app with the tenant mode as dedicated.
For the authorisation check, we now add
- two scopes (Display, Update)
- two role templates (Viewer, Manager)
- two role collections (Viewer, Manager)
After we have deployed the app, we will need to assign these role collections to our user. The scopes are references in the business logic app.
Update XSUAA Service Instance
To update the XSUAA service instance we can use either the CLI or the SAP Cloud Platform cockpit.
Command Line
As before, we can pass the JSON either in-line or as file.
As documented
cf service myxsuaa
cf update-service myxsuaa -c security/xs-security.json
SAP Cloud Platform cockpit
Alternatively, go to Service Instances in your Cloud Foundry space, select the instance, select update, and either upload the JSON file or provide it in-line.
Business Logic App | Node.js
Database | Users.json
We have seen that for authorised users, the UI returns a list of users and an option to add users. Most often, you would use some kind of database for this but to keep our example simple we are going to use a file.
To get started, create a file named
users.json in the business logic app.
Application Descriptor | Package.json
For our app to make sense of the JSON passed in the body of REST API POST call we need a body parser. Below the code from the
addNewUser(token) function in index.html where the name is converted to a JSON string.
jQuery.ajax({
url: '/myapp/users',
type: 'POST',
headers: { 'x-csrf-token': token },
contentType: 'application/json',
data: JSON.stringify({ name: name })
})
Body-parser is already included with express but we can install it to update the package to the latest version and have it listed as dependency in the application descriptor file
package.json.
npm i body-parser
As documented
Business Logic | Server.js
Update the business logic of the app to include an app.get and app.post function corresponding to the UI. For both a scope check is performed, as defined in the security descriptor (xs-security.json).
- non authorized users get a 403
- app.get returns the content of the users.json file.
- app.post adds a line to the file.
The Container Security API method and properties (checkScope) as documented
App Deployment
Command Line
To deploy the app run the cf push command. No changes were made to the manifest except for the removal of the hdicontainer-1 service instance as we are not using the SAP HANA Cloud database here in this example.
# housekeeping
cf d[elete] myapp -r -f
# deploying
cf push
# running
cf a[pps]
Unauthorized
As before, accessing the business app directly (myapp) returns HTTP 401 Unauthorized and accessing the approuter /myapp URL forwards the request to the identity provider for authentication.
Authenticate
As before, when we access the approuter URL the request is forwarded to the identity provider.
UI | Approuter Home
After log on, the home page (index.html) of the app router displays where we can view the list of users (file users.json) or add users.
UI | Forbidden
However, without authorisation access is forbidden. In other words, our account has been authentication but is not authorised (yet).
This is the result of the the
app.get method which returns Forbidden in case the required scope is not found in the JWT.
app.get('/users', function (req, res) {
var isAuthorized = req.authInfo.checkScope('$XSAPPNAME.Display');
if (isAuthorized) {
res.status(200).json(users);
} else {
res.status(403).send('Forbidden');
}
});
Scopes, Roles, Role Templates and Role Collections
SAP Cloud Platform Cockpit | APPNAME > Security
Above, we configured the XSUAA service with a security descriptor (
xs-security.json), which defined two scopes (<appname> Display and Update) and two role templates with role collections (Viewer, Manager).
As we have bound (
manifest.yml) both approuter and myapp to myxsuaa, a service instance of XSUAAA, both the roles, scopes, and role templates are listed under App Name > Security.
SAP Cloud Platform Cockpit | Subaccount > Security > Trust Configuration
As we have seen,
authentication for the Cloud Foundry environment (the subaccount that is which corresponds to a Cloud Foundry Org) has been delegated to an identity provider of which the default for the SAP Cloud Platform is the SAP ID Service.
To authorise users for our business logic app, we need to add a role collection to the user.
SAP Cloud Platform Cockpit | Subaccount > Security > Trust Configuration > [IdP] > Role Collection Assignment
By default on the trial environment, there is only a single user (your account) with a single role collection granted (subaccount administrator). When we select the IdP (
sap.default), Role Collection View Assignment is displayed. Here we can view and assign role collections
SAP Cloud Platform Cockpit | Subaccount > Security > Roles
At the subaccount level, the roles (and role templates) are listed under Roles.
SAP Cloud Platform Cockpit | Subaccount > Security > Role Collection
When the role collection already exists, this can also be done from Subaccount > Security > Trust Configuration > [IdP] > Role Collection Assignment.
The assignment is also displayed under APPNAME > Security > Roles
Business Logic App | Node.js
Viewer
With the Viewer role granted, the Show Users link on the approuter home page now returns a result.
As expected, adding a user still returns 403: Forbiddden.
This alert is triggered by the
addNewUser function on the approuter home page.
function addNewUser(token) {
var name = jQuery('#name').val() || '--';
jQuery.ajax({
url: '/myapp/users',
type: 'POST',
headers: { 'x-csrf-token': token },
contentType: 'application/json',
data: JSON.stringify({ name: name })
})
.done(function() {
alert( 'success' );
window.location = '/myapp/users'
})
.fail(function(jqXHR, textStatus, errorThrown) {
alert('Error adding new user: ' + jqXHR.status + ' ' + errorThrown);
});
}
Manager
When we add the Manager role to the myapp role collection to which our user is assigned, we can also perform the POST request.
Alternatively, create a new role collection, add the Manager role, and grant the role collection to the user.
Business Logic App | Python
The sample Python Business Logic app is less sophisticated as we only perform an authorization check in the business logic on the
openid scope. As the SAP ID Service uses OAuth 2.0, this is assigned to the user by default.
Business Logic | server.py
For our Python app, we add
sap-xssec and perform an authorization check on the security context and openid scope. If not authorized abort, otherwise proceed with a database query.
def db():
if 'authorization' not in request.headers:
abort(403)
access_token = request.headers.get('authorization')[7:]
security_context = xssec.create_security_context(access_token, uaa_service)
isAuthorized = security_context.check_scope('openid')
if not isAuthorized:
abort(403)
As documented
Note that we can call env.get_service by label or name (amongst others).
Application Router
We continue with the approuter as configured in the previous blog where we installed the
@sap/approuter package and defined the route(s) in the xs-app.json file,
User Interface | Index.html
The 'home" page of the approuter simply displays two links:
- app.route('/') - returns
- app.route('/db') with authorization check.
App Deployment
Manifest
As before, no changes were made to the manifest. App binds to the service instances for SAP HANA Cloud and XSUAA.
Command Line
To deploy the app run the cf push command.
# housekeeping
cf d[elete] myapp -r -f
# deploying
cf push
# running
cf a[pps]
403 Forbidden
We did not require authorization or authorization for the home page [ app.route('/') ]. However, when we attempt to access myapp/hana [ app.route('/db') ] HTTP 430 Forbidden is returned.
When we access the approuter, we first need to authenticate (as before).
UI | Authenticated
After authentication, the approuter home page is displayed.
UI | Authorized
To access the database, only the
openid scope is required.
Business Logic App | Java
Spring Boot
For an example using Java, see
Please Proceed
We are done with the tutorial series but should you want to know more about buildpacks, runtimes, vendor dependencies, application routes, and other application attributes which were slightly off-topic for our main story, visit
Share and Connect
Questions? Please post as comment.
Useful? Give us a like and share on social media.
Thanks!
If you would like to receive updates, connect with me on
For the author page of SAP PRESS, visit
Over the years, for the SAP HANA Academy, SAP’s Partner Innovation Lab, and à titre personnel, I have written a little over 300 posts here for the SAP Community. Some articles only reached a few readers. Others attracted quite a few more.
For your reading pleasure and convenience, here is a curated list of posts which somehow managed to pass the 10k-view mile stone and, as sign of current interest, still tickle the counters each month.
|