Skip to Content
Technical Articles
Author's profile photo Marius Obert

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.

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%20the%20summary%20video%20on%20YouTube

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

Assigned tags

      35 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Gregor Wolf
      Gregor Wolf

      Great that you reuse my bookshop sample :-).

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Of course, it's a perfect sample app!
      ?

      Author's profile photo Fabiano Rosa
      Fabiano Rosa

      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

       

       

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      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

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      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"}

       

      Author's profile photo Fabiano Rosa
      Fabiano Rosa

      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

      Author's profile photo Fabiano Rosa
      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

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Thanks for sharing the feedback!

      Author's profile photo Alfonso Armenta
      Alfonso Armenta

      The 'right kind' of IdP... I'm putting together a proof of concept of using a third party IdP (SAP ID is nice, but doesn't cut it for things like 2FA, password strength, etc). We will probably end up using IAS, but I'd like to know if it is truly possible to use a different IdP. So I'd really appreciate a couple of examples of the 'right kind' of IdP.

      Author's profile photo Jan-Willem Kaagman
      Jan-Willem Kaagman

      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

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      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.

      Author's profile photo Steven De Saeger
      Steven De Saeger

      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

       

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      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.

      Author's profile photo Steven De Saeger
      Steven De Saeger

      Hey Marious,

      Glad I could help 🙂

      Yeah we are using SAP identity authentication service as the main IDP ...

       

       

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      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.

      Author's profile photo Sreekanth Anumula
      Sreekanth Anumula

      Hi Marius,

      I have followed the same steps, but still I am getting 'Unauthorized'. Seems my service is not using the access token that I have sent. I have started my CAP service with the command 'cds run'

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Hi Sreekanth,

      It should work. Are you sure you followed all the instructions and that you use the default IdP?

      Author's profile photo Sreekanth Anumula
      Sreekanth Anumula

      I am using the Custom IDP. Its IAS

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Sorry for the late reply, I must have forgotten about this one. The mentioned approach only applies to the default IdP and LDAP IdPs (see first comment under this post).

      Author's profile photo Sandeep Malhotra
      Sandeep Malhotra

      Marius Obert  Thanks for detailed blog . I have basic query , Shall not we access the service via app router (in postman as well) . If not, why so ?

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Hi Sandeep,

      No, it's easier to skip the app router because Postman is capable to communicate directly with the srv component. Our goal is to test the API and not to test the authentication flow.

       

      Author's profile photo Cristian Abad Chico
      Cristian Abad Chico

      Marius Obert

      Great blog. Very helpful :).

      A couple of questions.

      • Is there any difference in the way the token is received depending if the roles are assigned at CDS entity level with @restrict or if they are assigned at CDS service level with @requires? I initially have it working whilst I had the roles assigned at service level with @require but now I moved the restriction at entity level and it is not working any more. I am able to retrieve the token but the actual call fails with forbidden. The entity is configured as follows:
      @(restrict: [
          { grant: ['READ','WRITE'], to: 'admin' },
          { grant: 'WRITE', where: 'userId = $user' }
        ])
      • How does it work when we want to retrieve a token for an 'authenticated-user'? For example if we had two services as follows:
      @path : '/locking-service'
      @requires: 'authenticated-user'
      service locksService {
        entity locks as projection on l.locks;
      }

      Many thanks in advance!

       

      C.

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Hi Christian,

      no, it shouldn't make a difference as Postman is just acting as an OAuth client here and CAP as the server. In case you notice that the CAP program does something wrong here, please open a new question to the CAP team here.

      Author's profile photo Cristian Abad Chico
      Cristian Abad Chico

      Many thanks for your prompt to reply. I will explore further and will raise a question to the CAP Team if required.

      Taking advantage of it I will ask you another question.

      I have seen in the comments above that there is a limitation in using Password Credentials if using some external IDPs. Is it case as well with Azure Active Directory?

      Thanks!

      C.

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      I'm afraid I haven't tried this with Azure AD, sorry.

      Author's profile photo Simon Gaudek
      Simon Gaudek

      Hi Christian,

      Azure AD ist NOT working for me.

      Regards
      Simon

      Author's profile photo Cristian Abad Chico
      Cristian Abad Chico

      Thanks for the confirmation Simon,

      I believe that it is something that SAP will have to address as lots of customers are using 3rd party Identity providers.

      Regards,

      C.

      Author's profile photo Simon Gaudek
      Simon Gaudek

      Hi Marius Obert

      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.

      Is this a limitation of the SAP BTP or SAML?

      The SAP Note describes the use of the IAS as an alternative.
      Do you know if the password grant works if the Azure AD is connected via the IAS (SAP BTP -> IAS -> Azure AD). Can I perform a password grant via the IAS for users in Azure AD, or can I perform the password grant only for users in the IAS?

      Regards
      Simon

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Hi Simon,

      I'm afraid I don't know for sure as I'm not super deep in security topics.

      Sorry

      Author's profile photo Minjie Lao
      Minjie Lao

      Hi Marius,

       

      Can you give some idea of the meaning of '.admin' here?

       

      Is this the "Application name" of role collection or just a fixed value?

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

      I am asking the question because am trying the postman call of my app.

       

      I tried to get the token via different post api call, but getting invalid scope error

       

      {
          "error": "invalid_scope",
          "error_description": "prj_bas_wsdata!t83438.admin is invalid. Please use a valid scope name in the request"
      }

       

      Here is my roll collection which working fine with my app.

      It will be appreciated if you can give some hints.

       

       

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Hi,

      "admin" is a scope that I defined, so it could be any other string as well.

      You will find this value in the definition of the CustomerService above. Note that I used the CDS tools to generate the xs-security.json file that creates this scope during the service provisioning.

      Author's profile photo Minjie Lao
      Minjie Lao

      thanks so much of the information. Clear now!

      Author's profile photo Mohammad Saad Rashid
      Mohammad Saad Rashid

      Hi Marius,

      Great blog indeed.
      I understand that CAP can support mocking out of the box but can you also shed some light on how one would achieve mocking XSUAA Auth Flow without CAP.

      I am using JAVA SpringBoot and XSUAA works seamlessly with Spring Security [in Production] but I am unable to find the right way to generate Bearer Tokens for Development Environment and then validate them as well.

      Regards,
      Saad

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      I'm afraid I'm not a Java expert (and also not familiar with SpringBoot) 🙁
      My guess would be that authentication in development environments doesn't use xsuaa at all or that it works with basic auth instead of OAuth. This is what CAP does it in the Node.js flavor but I'm not sure about Java/SpringBoot.

      Maybe this part of the CAP Java documentation can help you.

      Author's profile photo Mohammad Saad Rashid
      Mohammad Saad Rashid

      Thanks for the quick reply Marius.
      Even in case of node.js how would you achieve this without CAP, my guess is have different code lines for local/dev and prod environment, which sounds a little tricky. Not sure if its the best practice.

      Nevertheless I have asked the same on the community.
      For anyone interested you can find the question here --> How to mock XSUAA for Local Machine for SpringBoot Application without CAP ? | SAP Community