How grant-types keep your application secure – Exercise 1

This post has the first set of exercises demonstrating different grant types described in my article “How grant-types keep your application secure?“. This exercise looks at the grant types that derive authorization directly from the user. The lab exercises are for examining the influence of configuration parameter grant-type. You may use this as a setup for further exploration. I would be curious to know what you explored. Please share your own experiments in the comments.

Pre-requisite:

You have completed the lab setup as outlined in the post.

Introduction

I found httpyac to be a great tool for experiments with oAuth. The file .env you created during lab setup is for configuring the tool httpYac. This is to avoid typing the configuration every time. We will use this tool extensively in the exercises.

In this exercise we will examine the following grant types:

  • Client Credentials grant
  • Authorization Code grant
  • Password grant

Client Credentials grant

Start in the terminal with the directory you created in the lab setup and enter the command.

npx httpyac oauth2 --prefix blApp 

Side Note:

The argument blApp is the prefix we gave for entries in .env file for values from the keys for cf-application-uaa.

Note that the command succeeded in getting a token. It prints out the token. It would be pointless for me to reproduce the token here. This token is for accessing the Application bound to cf-application-uaa. Lets inspect the token.

httpyac oauth2 --prefix blApp | node decode-jwt.js

Sample output:

{ "jti": "53025a093c3549b68cb6a5f919f17850", "ext_attr": { "enhancer": "XSUAA", "subaccountid": "3caxxxxe-4c10-488e-xxxx-2877xxxxf6a6", "zdn": "provider-2022" }, "sub": "sb-cf-application!t53187", "authorities": [ "uaa.resource", "cf-application!t53187.Excercise_System_Scope_1" ], "scope": [ "uaa.resource", "cf-application!t53187.Excercise_System_Scope_1" ], "client_id": "sb-cf-application!t53187", "cid": "sb-cf-application!t53187", "azp": "sb-cf-application!t53187", "grant_type": "client_credentials", "rev_sig": "da756d98", "iat": 1658155869, "exp": 1658199069, "iss": "https://provider-2022.authentication.us10.hana.ondemand.com/oauth/token", "zid": "3caxxxxe-4c10-488e-xxxx-2877xxxxf6a6", "aud": [ "uaa", "cf-application!t53187", "sb-cf-application!t53187" ]
} 

Note the following fields:

  • iss – issuer of the token
  • aud – audience of the token
  • azp – Authorized party – the party to which the Token was issued issued
  • cid – client for whom this token was issued
  • client_id – client for whom this token was issued
  • zid – ID of the identity zone / subaccount

These are the fields client library will validate after ensuring the signature (skipped in the output) matches and the that the token is still valid (fields iat and exp). Note also the following fields:

  • scope – what are the authorizations
  • grant_type – what is the grant type that authorized the token

The values of the field scope is what is checked when the application checks for authorizations. Scope declarations come mostly (apart from some famous global names) from the definition of the client. Look in the file xs-security.json for configuration of the client and compare the content of scope. One of the scopes is Excercise_System_Scope_1 prefixed with the xsappname ( this was noted in .env file for reference). Note that this scope is mentioned in the configuration as below:

 "authorities": ["$XSAPPNAME.Excercise_System_Scope_1"],

The grant type client credentials provides a token without a user context. The authorizations a token from client credentials grant get is from what is configured as authorities for the client in configuration.

Extra credit:

Deploy after changing configuration so that authorities is empty ([]) in configuration and see the scope of the token issued.

Note that one of the items in the scope is uaa.resource. This is one of the famous global scopes for tokens issued with client credentials grant.

Authorization Code grant

Continue in the terminal entering the following command:

httpyac oauth2 --prefix approuter --flow authorization_code

Side Note:

The argument approuter is the prefix we gave for entries from keys for cf-application-uaa in .env file.

The default for –flow for the tool is client_credentials. So this was not mentioned last time.

This opens a browser window and asks for you to login. On successful completion of login, the command prints out a token. The tool performs the dance involved in interacting with the browser to get authorization code and requesting for token. Since the tool does this, we don’t care about this dance. Approuter orchestrates this dance in a typical application in SAP BTP.

Lets look at the token. Inspect the token by decoding it.

httpyac oauth2 --prefix approuter --flow authorization_code | node decode-jwt.js

Note that additional fields about the logged in user are present in the token.

  • email
  • user_id
  • given_name
  • family_name
  • origin – the key of the identity provider which authenticated the user

Further scope now includes 2 famous global entries:

  • uaa.user
  • openid

These scopes allows the token to be used to access the user’s details listed earlier. These are also need for propagating user authorization to downstream applications with grant types we will look at in later exercises.

Side Note:

In the .env file, we had made an entry approuter_scope with value ” “. With this the client is requesting for including all scopes it is authorized for in the token. By default the tool only requests for scope openid. In a typical authorization code flow for oAuth 2.0, the user would be asked to approve the scopes the application is requeesting for. In the configuration for cf-approuter-uaa, in mta.yaml, we have set a parameter autoapprove: true.
The user will get a screen for approving the scopes if this parameter is set to false. Default value for this parameter is true. It is normal for Enterprise applications requesting authorizations from a trusted oAuth authorization server to have this default setting. Approval from user is
relevant when the authorization is for access from not trusted applications, the user may not be familiar with.

Note that no scope declared in the configuration is in the list of scopes in the token. A scope is added to a token issued for a user only if a roles containing it is granted to the user. We have declared a role collection in xs-security.json and this in turn refers to a template that includes a scope declared in the configuration. Lets assign the role collection Excercise_Role_Collection_1 to the user and see how the list of scopes in the token changes.

Refer to the blog Demystifying XSUAA in SAP Cloud Foundry on how to assign
a role collection to a user using SAP BTP Cockpit. It is easy to assign a role collection to a user using SAP BTP command line interface (btp CLI) too as shown below. If the user is not from the default identity provider, substitute the key for the identity provider in place of sap.default.

# $subaccount is the id of the subacoount and # $user is the user id (usually an email) of the user
# in IDP sap.custom. Here sap.custom is the key for the trusted IAS tenant assigned by assign trust button. # For SAP ID service the key is sap.default btp assign security/role-collection Excercise_Role_Collection_1 --to-user $user --of-idp sap.default --subaccount $subaccount

Inspect the token after assigning the role collection to the user.

httpyac oauth2 --prefix approuter --flow authorization_code | node decode-jwt.js

Note that the scope from the configuration is still not listed in the field scope. Why? The scope in the
assigned role collection is for the client of cf-application-uaa. It is not referred by the client of cf-approuter-uaa. The only scope referred in the configuration of cf-approuter-uaa is uaa.user. This was already listed in the scope before role assignment. This scope is assigned to every user. So the client just had to make a reference to it for getting this included in the scopes of tokens it requests.

Extra credit:

Scopes from other configurations can be included in another configuration. This done with configuration parameter foreign-scope-references. Add the scope Excercise_User_Scope_1 from the configuration of cf-application-uaa to the configuration of cf-approuter-uaa and check what difference it makes to the entries for scope. You will have to add granted-apps in the original scope definition in xs-security.json like below to allow this reference.

"granted-apps" : [ "$XSAPPNAME(application,cf-approuter)"]

Refer to the the documentation for details.

Revert these changes as these are not typically  required for accessing Business Logic Applications. This is what the exercises will demonstrate.

Break Something

All good so far. But it is not an experiment unless something breaks. Lets break something.

Note the values of configuration parameter grant_types in configuration (mta.yaml) of cf-application-uaa and cf-approuter-uaa.

 - name: cf-application-uaa config: oauth2-configuration: grant-types: - client_credentials - name: cf-approuter-uaa config: oauth2-configuration: grant-types: - authorization_code

The former does not include authorization_code and the later does not include client_credentials.
Repeat the previous steps for acquiring a token by exchanging the clients used when requesting tokens with these grant-types.

httpyac oauth2 --prefix approuter --flow client_credentials
no valid auth response
httpyac oauth2 --prefix blApp --flow authorization_code Browser reports:
Authorization Request Error
There was an error. The request for authorization was invalid.

We broke something. This proves that it is necessary for the clients to be configured to use the grants.

Password Grant

Password grant is used for getting authorization when the user interaction flow using a browser is not feasible. There are two variations possible for this grant type. One involves sharing user credentials with the client and the other shares one time password. Former is useful when user interaction in any form is not not an option. This is for example the case of system to system calls in the context of a technical user.

Copy the contents below as file mta-ext-ex1d.yaml

---
_schema-version: '3.1'
ID: cf-application-ext-1d
extends: cf-application
version: 1.0.0 resources: - name: cf-approuter-uaa parameters: config: oauth2-configuration: grant-types: - password - authorization_code

Execute the following commands to deploy the package with the extension descriptor copied in the earlier step. The extension descriptor, adds password as a allowed grant type to cf-approuter-uaa. You can check that this grant type is added to the oAuth client with SAP BTP command line interface (btp CLI) as shown below.

> cf deploy cf-application_1.0.0.mtar -f --no-start -e mta-ext-ex1d.yaml
.. > btp get security/app cf-approuter!t53187 --subaccount $subaccount
.. grant-types: - password - authorization_code
..

The credentials acquired earlier from the key would inherit these changes. So we can proceed to use the newly acquired grant type.

Copy the contents below to file exercise-1d.http

### Password
@login_hint={{ JSON.stringify({"origin":"sap.custom"}) }}
# @name token_response
# @jwt access_token
POST {{approuter_url}}/oauth/token
Content-Type: application/x-www-form-urlencoded
Accept: application/json
Authorization: Basic {{approuter_clientId}} {{approuter_clientSecret}} grant_type=password
&username={{$prompt Enter Username}}
&password={{$password Enter Password}}
&login_hint={{encodeURIComponent(login_hint)}} ### Passcode
{{ const open = require("open") const passcode_url = `${approuter_url}/passcode` open(passcode_url);
}}
# @name token_response
# @jwt id_token
POST {{approuter_url}}/oauth/token
Content-Type: application/x-www-form-urlencoded
Accept: application/json
Authorization: Basic {{approuter_clientId}} {{approuter_clientSecret}} grant_type=password
&passcode={{$password Enter Passcode}}

This file describes two requests for access token: one using password and another using a temporary authentication code. Lets make the requests and see with the following command.

Warning: without -o body in the command below, the password is copied to output in clear text.

httpyac exercise-1d.http -o body

Use the arrow keys to select the first option “Password”. It prompts for Username and Password. With these the request is made and the response is displayed. The response contains access token. This is decoded and displayed.

Change sap.customto sap.default in file exercise-1d.http (line #2) to use the SAP ID service (Default identity provider) as the identity provider. Only keys sap.custom or sap.default  can be used for password based authentication this way.

httpyac exercise-1d.http -o body

Use the arrow keys to select the second option “Passcode”. It prompts for Passcode.

It also opens a request for a temporary authentication code in browser. You have to authenticate to receive a passcode. If you had configured multiple identity providers, note that you had to choose the identity provider for login before authenticating. Here you could choose identity providers using SAML protocol as well. The passcode issued is for the chosen identity provider.

Copy and Paste the temporary authentication code from the browser to the terminal.  Now the request for token is send with passcode. The access token is decoded and included in the the output like before.

Note that the access_token has the field values for relevant fields in both cases. These are different from the access_token from authentication code only in the field grant_type. The resource server can identify if the client used authorization code grant or password grant, but it cannot distinguish between use of passcode or password for authenticating for password grant.

Note that users (fields sub and user_uuid in jwt) from different identity providers are different for XSUAA service even if they have the same attributes. Roles have to be assigned to these users independently. But, the resource servers (the business logic application)  may choose to ignore the difference and identify users based on a attribute like email.

Key Learnings

  • How to use the tool httpyac to get access tokens with grant-types client_credentials, authorization_code and password
  • How to decode jwt tokens and what are the important fields in jwt
  • When configuration parameter authoritiesis relavant for scopes in jwt
  • How some role assignments are irrelevant for scopes in jwt
  • How the configuration parameter grant-types controls which token requests are accepted by XSUAA
  • Which grant type is required by Application Router for user authentication.

Next Steps

Did you understand a bit more about XSUAA service with this exercise? Please let me know in the comments.
Proceed with the other lab exercises to examine further: