Skip to Content
Technical Articles

Using Postman for API Testing with XSUAA

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.

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.

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 Cloud Platform. I can recommend this blog post if you want to learn more about the User Authentication and Authorization in SAP Cloud Platform.

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

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 Cloud Platform 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 Cloud Platform trial (if you haven’t done so already):

1. Get Postman

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

2. Deploy Gregor Wolf‘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 Cloud Platform 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 Cloud Platform account.

4. Try to read data

4.1 Use the SAP Cloud Platform 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.identityzone + xsuaa[0].credentials.uaadomain + "/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.

14 Comments
You must be Logged on to comment or reply to a post.
  • Hi Marius, great content and very helpful to the developers!

    But we have some limitations when the SCP Cloud Foundry Sub-account is configured with a custom Trust configuration (instead of the default SAP ID Service) like a Corporate IdP, because with this setup we cannot retrieve the JWT Token with Postman using Password grant type of OAuth2.

    Do you have some suggestion to do it with this setup?

    Regards,

    Fabiano Rosa

     

     

    • Hi Fabiano,

      sorry for the late reply. To be honest, I haven’t considered this (very valid) scenario so far. I’ll try to find out more and get back to you here.

       

      Regards,

      Marius

    • I checked and it is possible with a manual workaround if you have the “right” kind of IdP. It depends on the protocol your corporate IdP speaks (LDAP is fine, SAML won’t work).

      The bad news is that the Postman “Get Token” dialog doesn’t support additional fields. So you would have to send a new POST request to the access token URL for retrieval of the JWT token. To do this, send the following values in the body of this request:

      In my screenshot, you see the error message that you’ll get when the IdP doesn’t support the password-grant flow

      For the field “login_hint”, you need to replace “http…net: with the “Origin Key” of your corporate IdP (you’ll find this info in the SCP Cockpit). The value of this field is simply a base64-encoded JSON string:

      {"origin":"YOUR-ORIGIN-KEY"}

       

      /
      • Thanks for sharing Marius, I will try to get the token with the “login_hint” parameter using SAP IAS as a IdP on SCP CF.

        Regards,

        Fabiano Rosa

        • Hi Marius, just sharing a feedback from my tests:

          login_hint parameter

          You need to send the field “login_hint” in the request without base64-encoded, just in a JSON format like “{“origin”:”YOUR-ORIGIN-KEY”}”, as described in Cloud Foundry documentation:

          https://docs.cloudfoundry.org/api/uaa/version/74.16.0/index.html#password-grant

           

          Error when using the “login_hint” parameter

          In my scenario I have two Trust Configurations enabled in the CF subaccount: the SAP ID Service and a SAP IAS, so when I try to send the “login_hint” with the origin key for SAP IAS, I get this error:

          {
          "error": "unauthorized",
          "error_description": "The origin provided in the login_hint does not match an active Identity Provider, that supports password grant."
          }

           

          SAP Cloud Platform Cloud Foundry limitation

          Unfortunately, I found this ticket from SAP that describe that the password grant_type is not supported with custom IdP in SCP Cloud Foundry, as documented at https://launchpad.support.sap.com/#/notes/2766354, and for now this grant type only can be used with the SAP ID Service.

          Regards,

          Fabiano Rosa

          /
  • Nice blog, and very instructive. Learned a lot here 😉 . We sometimes use postman to do some regression testing using the postman collections. So far I have not been able to switch users in one collection, did you try this?

    regards,
    Jan-Willem Kaagman

    • I’m glad you liked the post!

      To be honest, I haven’t tried this yet. But it seems that it is possible that request inherits the auth mechanism from the collection. So it should be possible to set up one collection per user. I’m not sure if it’s possible to have multiple users in one collection, for this you might have to use copies of the same collection.

  • Nice Blog Marius.

    Just a quick remark .. could it be there is a reference error in the point 5.4

    Shouldn’t it be

    xsuaa[0].credentials.<field> 

    to access the specific fields ?

    I actually noticed this as I was getting excited about a potential solution for a problem I have with the scope fields – see my question https://answers.sap.com/questions/12850767/postman-oauth-call-with-custom-scopes.html but unfortunately I think I already tried the format you are describing here.

    Thanks,

    Steven

     

    • Hi Steven,

      you are right, the “credentials” are missing. I’ll fix this right now. Thanks 🙂

       

      I had a look at your question and it should actually work as described here. Are you using a custom IdP? This could be a reason why it isn’t working.

        • Have you seen Fabiano’s question above? It seems like you’re sitting in the same boat. Unfortunately, he mentioned that the needed grant_type is currently not supported.