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: 
mariusobert
Developer Advocate
Developer Advocate
37,386

In this post, I will show a trick which you can use to fetch JSON Web Tokens from the User Account and Authentication service with Postman. This simplifies API testing as you'll no longer need to redirect incoming traffic via the approuter. You also won't have to intercept and expose JWT (pronounced "jot") tokens from the approuter any longer.


Updates:

29th Jan 2021: Rebranding to SAP BTP

funny time saver gif

To most developers, web security is a rather unpopular topic. Everyone agrees it's necessary, but no one really likes to do it. There's a lot of boring stuff you need to know, you see little to no "real" process in your app even when you spend a fair amount of time on it. And on top of all of that, it makes development and testing harder as you either have to mock the authentication or simulate a real user log on.

But it doesn't have to be hard: If you use the right backing services (XSUAA), you won't have to deal with the authentication. If you use the right framework (CAP), you won't have to deal with mock or production authorization. And if you use the proper tooling (Postman), you won't even have to bite the bullet for testing.

I know I just threw a bunch of buzzwords at you, and there's a lot to unpack. The next few paragraphs will explain each component and provide more background links. If you are already familiar with the terms in bold and, just want to learn how to use Postman to fetch JWT tokens from the XSUAA server, feel free to jump directly to the hands-on.


Watch the summary video on YouTube



What is a JWT Token


JSON web tokens and the other concepts I'll explain in this paragraph are standardized and exist far beyond the "SAP world" and even outside of the "Cloud Foundry universe." JWT (pronounced: jot) tokens are the de-facto standard for authentication in modern web applications. I don't want to go into detail here, so I only try to give a short definition:
A JWT token is a manipulation-proof, signed JSON object that contains standardized properties like user information and access rights.

In Cloud Foundry, this token is issued by the User Account and Authentication (UAA) server. In the case of SAP CP Cloud Foundry and SAP HANA XSA, we call this service also XSUAA.

A typical business application would use the approuter as the central point of entry, which checks if the user is signed in. If the user is not signed in, it will (1) request the authentication from the IdP, (2) request the JWT token from the XSUAA, and (3) attach this token to all following requests of this user. It is worth highlighting that the UAA service only issues the token, but it does not authenticate the user. Through this decoupling, any identity provider (IdP) can be connected to the XSUAA - and, therefore, to SAP BTO. I can recommend this blog post if you want to learn more about the User Authentication and Authorization in SAP BTP.


XSUAA sequence chart


I already mentioned that the token contains information about the owner, such as his or her name, email address, and access rights (scopes). You can compare a token to the key (card) you use every day to access your office. The JWT token is cryptographically signed by the UAA server, which means adversaries cannot alter the user information of a token. However, if they somehow get access to a token, they can cause a lot of harm as they can access all the data the user has access to. To counter this threat, the access tokens are usually only be passed between software components and not exposed to the user! This is the reason why the approuter attaches the JWT token to the request.

To summarize, the approuter enforces that the current user is signed in and attaches the JWT token it received from the XSUAA to all requests that are redirected to other services.

What is Postman


Postman is a top-rated tool for API testing. Especially when you are developing an application that has to expose an API (which app does not nowadays?), Postman can make your life a lot easier. It allows you to create, save, and execute HTTP requests and request collections. The execution of saved request collections can be used to trigger an arbitrary behavior of your API. With this, you can use it for testing as well as for frequently-performed data manipulations.


While terminal-based testing can be compelling as well, Postman offers a developer-friendly user interface. This interface makes it easy to understand how the various parameters of sent requests work. Additionally, Postman provides a wide variety of pre-configured authentication flows, which can be used to test secured APIs. Testing with Postman can be referred to as semi-automated. Postman does most of the heavy lifting, but there is still some user interaction involved, e.g., to trigger the authentication flow or the selection and execution of the request collections.



Securing APIs in Business Applications


I already mentioned that these concepts are standardized and are not SAP-specific. In theory, you could implement an application from scratch and only use open-source tools from the community to perform standard tasks such as token validation. But it obviously also makes sense to use tools that offer more SAP-focus and abstraction. The @sap/xssec package is such a package that already makes assumptions about the SAP Cloud Foundry xsuaa service. However, in most cases, you don't even want to implement the authentication check manually. Instead, you just want to annotate your services with the required roles. And this is precisely what the Cloud Application Programming Model (CAP) is designed for! With simple annotations, you can specify which services shall be restricted to which user roles:
service CustomerService @(requires: 'authenticated-user'){
entity Orders @(restrict: [
{ grant: ['READ','WRITE'], to: 'admin' },
{ grant: 'READ', where: 'buyer = $user' },
]){/*...*/}
entity Approval @(restrict: [
{ grant: 'WRITE', where: '$user.level > 2' }
]){/*...*/}
}

Another strength of CAP is that it saves you the roundtrip to the cloud as there's always a "local alternative" to cloud services. The local alternative to SAP HANA is SQLite, to the Enterprise Messaging service it's file-based messaging, and to XSUAA, it's mocked users with basic auth. I recommend using mocked authentication during local development as this ensures that authentication is actually always on - and not only in production. Additionally, basic authentication is quite easy to mimic in your test suite.

However, there are always situations in which you need to test the holistic service that runs in the cloud with an xsuaa service instance connected to it. In these situations, you often run into the problem that the deployed service will reject all traffic that doesn't contain a JWT token. You have the following options to deal with this:

Approaches to Get a JWT Token


Redirecting the Traffic via the Approuter


This approach is quite intuitive. You basically interact with the application the same as an end-user would and send all your traffic to the approuter. At the very first request, the approuter will return a login-prompt from the identity provider. After the successful login, all the following requests for the API are redirected by the approuter, which will attach the JWT token automatically.

The advantage of this approach is its simplicity. Disadvantages of this redirect-approach, on the other hand, are that this requires a manual login that expires after a couple of minutes of inactivity. This makes it hard to apply semi-automated tests or to use Postman efficiently. Another disadvantage is that you need to deploy an approuter, even when there's no UI needed at all.

Intercepting the Token from the Approuter


If you want to test your API semi-automated, you have to have access to this JWT token. As already mentioned, this token should not be accessible to the end-user of your application. Therefore, we need a workaround and extend the standard approuter to return this token when we access a particular URL (here: /my-jwt😞
const Approuter = require('@sap/approuter')();

Approuter.beforeRequestHandler.use('/my-jwt', function (req, res) {
res.end(`Your token is: ${req.session.user.token.accessToken}`);
}).start();

On the first request, the approuter will still redirect us to a login page, but afterward, we'll be able to intercept the token. With this token, we can use Postman, with the "Bearer Token" authorization feature, to connect to the protected API directly. As long as the token is valid, we can send the requests directly to the API server, and we won't need to interact with the approuter anymore.



This approach is getting us closer to the solution we want to archive but also introduces a new disadvantage: We need to make sure that this new endpoint won't be shipped to production apps as end-users shall not be allowed to access their token.

Using Postman to request the Token








This approach only works when the IdP is using the right protocol (LDAP is fine, SAML won’t work). The default IdP fullfils this requirement.

I already mentioned that Postman comes out of the box with multiple authentication flows. The previous approach uses the Bearer Token method, which is quite simple as Postman only has to add an HTTP header field to all requests. Therefore, this was just for authorization but not for authentication. The good news is that Postman also supports entire authentication flows such as OAuth 2.0. And this is precisely the flow the XSUAA service implements.

This means we don't have to rely on the approuter any longer for token retrieval. Now we can use Postman to follow the same protocol steps as the approuter does when it reads the information about the xsuaa from the service binding (e.g., the environment variables). As for all Cloud Foundry services, service information can be found in two places:

  • In a service binding between a Cloud Foundry app and the xsuaa service instance

  • In a service key for the xsuaa service instance (not bound to an app)


Both places can be inspected either via the CF CLI or in the SAP BTP cockpit. This means we don't necessarily need an approuter, we don't introduce security concerns and as a bonus: this is also the most-user-friendly approach to test the API.

In the next section, we'll see how this looks in action:

Retrieve the JWT Token with Postman


0. Preparation


Before we get to the fun part, we need to install some tools which are mandatory for cloud development on SAP BTP trial (if you haven’t done so already):

1. Get Postman


If you haven't already done so, install Postman.

2. Deploy gregorw's bookshop


git clone https://github.com/gregorwolf/bookshop-demo
cd bookshop-demo
mbt build
cf deploy mta_archives/bookshop-demo_0.0.1.mtar

3. Create a role collection and assign it to your user


3.1 Open the SAP BTP cockpit (the link points to the trial landscape)


3.2 Navigate to the subaccount that you deployed the application to


3.3 Create a new role collection


3.3 Assign a the bookshop admin role to the collection


3.4 Navigate to the SAP ID user service



3.5 Add the role collection to your SAP ID user




Instead of (1), use the email address you used to sign up for the SAP BTP account.

4. Try to read data


4.1 Use the SAP BTP cockpit of the CF CLI to get the URL of the server application


cf apps | grep bookshop-demo-srv

This command should print a URL similar to this one:
https://<UserID>-cftrialjan20-dev-bookshop-demo-srv.cfapps.eu10.hana.ondemand.com/admin/Books

4.2 Append "/admin/Books" to this URL and send a GET request via Postman



4.3 Notice that we get an "Unauthorized" error


5. Get Access token via Postman


5.1 Open the "Get New Access Token" dialog



5.2 See the required input fields




You can already define a name for the Token and set the "Grant Type" to "Password Credentials." Further, enter the email address you used in step 3.5 in field 2 and the corresponding password in field 3.

5.3 Read the other values from the environment variable of the application


cf env bookshop-demo-srv

This will print a large JSON file. In there, find the object VCAP_SERVICES.xsuaa which looks as follows:
"xsuaa": [
{
"binding_name": null,
"credentials": {
"apiurl": "https://api.authentication.eu10.hana.ondemand.com",
"clientid": "sb-bookshop-demo!t34033",
"clientsecret": "XXXXXX=",
"identityzone": "cftrialjan20",
"identityzoneid": "a0295cd8-5447-4c7f-98af-3aa3abcc9ded",
"sburl": "https://internal-xsuaa.authentication.eu10.hana.ondemand.com",
"tenantid": "a0295cd8-5447-4c7f-98af-3aa3abcc9ded",
"tenantmode": "dedicated",
"uaadomain": "authentication.eu10.hana.ondemand.com",
"url": "https://cftrialjan20.authentication.eu10.hana.ondemand.com",
"verificationkey": "-----BEGIN PUBLIC KEY-----ABC-----END PUBLIC KEY-----",
"xsappname": "bookshop-demo!t34033"
},
"instance_name": "bookshop-demo-uaa",
"label": "xsuaa",
"name": "bookshop-demo-uaa",
"plan": "application",
"provider": null,
"syslog_drain_url": null,
"tags": [
"xsuaa"
],
"volume_mounts": []
}
]

5.4 Enter the missing values in the dialog


Access Token URL (3) = xsuaa[0].credentials.url + "/oauth/token"

Client ID (4) = xsuaa[0].credentials.clientid

Client Secret (5) =xsuaa[0].credentials.clientsecret

Scope (6) = xsuaa[0].credentials.xsappname + ".admin"

5.5 Request the token



5.6 You should now see that the call has been successful. Scroll to the bottom of the page and hit "Use Token"


6. Access the API via Postman


6.1 You already see the fetched token here. Now you need to add this token to the header of the subsequent requests


6.2 Click "Send" one more time to successfully fetch data from the secured API ?


Good job!

Summary


While all three approached can be used to get to the same result, there are some differences.































Works without approuter Postman usage possible The effort to renew the token Others
Redirect Traffic High
Intercepting the Token High Potential Security issue
Request Token Low

Disclaimer: For completion, I also want to mention that it is possible to fetch the token manually via HTTP requests, as indicated in an older post of mine. This would probably the most convenient way for fully automated testing. I personally prefer the approach above because I believe Postman offers many great features for a developer-friendly inspection and testing of a secured API.

Additional Resources


In case you want to do the same for SAP Hana XSA development, check out this video tutorial by thomas.jung. Here, he did the same to test an API that runs on the HANA platform.
39 Comments