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.
- 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 requesting for. In the configuration for cf-approuter-uaa, in mta.yaml, we have set a parameter |
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
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
sap.custom
to 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
Key Learnings
- How to use the tool httpyac to get access tokens with grant-types
client_credentials
,authorization_code
andpassword
- How to decode jwt tokens and what are the important fields in jwt
- When configuration parameter
authorities
is 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: