SAP AppGyver and Proof Key for Code Exchange (PKCE) or “Striving for enterprise-grade security in SAP low-code applications”
Building applications has never been easier. With the advent of low-code solutions like SAP AppGyver, anyone can create a beautiful UI and connect it to real business applications, such as SAP S/4HANA or SAP SuccessFactors. Taking these low-code apps beyond prototyping and into production requires additional focus on areas like application authentication and authorization as well as principal propagation to backend data sources for a secure and contextual experience.
Development of low-code applications can be made easier by leveraging open APIs for data access. These APIs will require authentication when connecting to productive systems though, and they will almost certainly be secured with OAuth 2.0. OAuth 2.0 is used across cloud and on-premise applications as the industry standard for authorization and is broadly supported by SAP Identity Authentication (SAP IAS) and the SAP Business Technology Platform Authorization and Trust Management Service (SAP XSUAA).
While there are numerous authorization grant flows (see RFC6749) when using OAuth 2.0, most of them require a client ID and client secret to be sent with the request, typically as a Base64 encoded Basic Authorization header. As many developers will tell you, this is a problem for clients that persist this information. A reasonable user of the app could easily capture the request headers and gain access to the client credentials, and with them, the ability to abuse a potentially privileged account. Fortunately, the OAuth specification accounts for this problem and offers the Proof Key for Code Exchange, or PKCE, for public clients such as SAP AppGyver, where we would like to avoid sharing a client secret.
In this blog (and the supporting GitHub repo: https://github.com/SAP-samples/appgyver-auth-flows), we will demonstrate how to:
- Configure an SAP Identity Authentication Service (IAS) application supported for public client usage and enabled for cross-consuming SAP XSUAA services
- Create an SAP AppGyver application that implements an authorization grant flow using PKCE, from an iOS device
- Manage access and refresh tokens, and use them with a protected SAP Cloud Application Programming (CAP) service running in the SAP BTP, Cloud Foundry runtime
Hint – There is also another sample in the GitHub repo showing you how to interact directly with the SAP XSUAA service if you don’t have an IAS tenant. Please be aware that this sample uses the standard Authorization Code flow for granting authorizations and is therefore not suitable for a productive enterprise scenario.
Let’s quickly check out the architecture and service requirements for the PKCE scenario before going into the details! Special thanks to my teammate, Martin Frick, for his immense contribution to this sample.
Architecture and requirements
Below you can find a simplified architecture of the scenario covered by this blog post and the provided GitHub repo. You will get a full understanding of the architecture pattern when reading through the rest of the story, so feel free to jump back to this diagram from time to time.
As briefly mentioned in the motivation, this blog post is supposed to give you an idea how to securely access your SAP CAP application running in your SAP BTP landscape from an SAP AppGyver app. Therefore, a few requirements and entitlements are mandatory. Whereas the Azure Active Directory integration acting as Identity Provider is optional (and not part of the step-by-step guide), the rest of architecture components are essential.
|SAP Identity Authentication|
|Cloud Foundry Runtime||MEMORY||1|
|Cloud Identity Services||service|
Trust Management Service
Let’s get started with a short explanation of why SAP Identity Authentication is used instead of SAP XSUAA in this scenario and give you a few more details on the PKCE process.
SAP Identity Authentication Service (IAS)
Let’s first discuss why SAP Identity Authentication Service is required for PKCE, whereas most other SAP BTP scenarios including SAP CAP-based services are built using SAP XSUAA.
SAP XSUAA is an SAP specific fork of the Cloud Foundry User Account and Authentication (UAA) service. While SAP XSUAA fulfills similar tasks as SAP IAS when it comes to authenticating a user and providing access tokens, for our scenario there is one major advantage of using SAP IAS. As of today, SAP XSUAA does not support the PKCE authorization grant flow for public clients, which is a key point for our use case.
So, what is PKCE and why is it important for this use case? As already explained, the Proof Key for Code Exchange (PKCE) authorization grant flow was developed for public clients (like mobile apps), which are not capable of securely storing the client secret (e.g. on a user’s device). It is a variant of the OAuth 2.0 Authorization Code flow for authorization grants, which allows a secure authorization mechanism, but without the requirement to provide the client secret when calling the token endpoint of the authorization server.
The PKCE flow follows RFC7636 (click here) and you will find more detailed information available across the internet that is probably better than any explanation we can give here in this blog post. So check out the oauth.net page for an example (click here) to learn more about this topic, and try out their nice playground (click here) to get a better understanding of the required steps.
As explained in detail in the provided links, the basic idea of the PKCE flow is to generate a random string on the client and send it to the to the authorization server as a so-called code challenge. The authorization server keeps track of the code challenge value (usually passed SHA256 encrypted) and after authorizing (meaning a user has logged in and given consents), it returns an authorization code to the client.
When the client now wants to obtain an access token using the authorization code, it has to provide the random string once again as a code verifier within the request parameters. The authorization server compares the code verifier against the stored code challenge. Only if both values match will it issue an access token to the client.
The standard OAuth 2.0 Authorization Code flow to obtain authorization grants for non-public clients (see RFC6749) requires the client secret when requesting an access token from the authorization server, whereas in the PKCE flow this is not required anymore. This mitigates the risk of your client secret being compromised, and all consequences of such a critical security issue! Private clients like web applications are usually capable of securely storing the client secret on the server side, but an attacker who decompiles an app stored on a public client may gain access to your client secret. As such, the PKCE flow has become a de-facto industry standard when it comes to public client authorization requirements.
So, let’s get back to our current scenario. As previously mentioned, SAP XSUAA does not support PKCE for authorization scenarios, but only the Authorization Code flow requiring a client secret. Therefore, in this blog we decided to use the SAP Identity Authentication service for a more realistic and enterprise ready scenario. As the SAP IAS is a major enabler of intelligent enterprise scenarios the additional time investment is worth it.
You might ask yourself now – “Sounds reasonable, but how do I get access to services in my SAP Business Technology Platform which are usually protected by SAP XSUAA and not SAP IAS?”. That’s a valid question which we will cover next. The magic word is cross-consumption..
SAP XSUAA cross-consumption
While most of the existing scenarios in the SAP BTP environment rely on authentication against SAP XSUAA (leveraging SAP IAS only as an Identity Provider) this scenario is different.
We’re interested in the PKCE approach, so the user in our use case will directly authenticate against SAP IAS, which will also handle the respective authorization grant flow. Consequently, the client will not receive an access token issued by SAP XSUAA but issued by SAP IAS. So how can this token be used to call services like an SAP CAP application, which is tightly integrated with SAP XSUAA for authentication and authorization purposes?
For these types of scenario, a feature called cross-consumption exists. It allows you to build interesting use cases relying on SAP XSUAA while at the same time using your SAP IAS instance for authentication and authorization. You just need to ensure that a few requirements are fulfilled, which make it possible to validate tokens issued by SAP IAS in an SAP XSUAA-coupled environment like CAP.
- A trust between SAP XSUAA and SAP IAS needs to be established (click here). Please use the OpenID Connect approach for this instead of manually exchanging SAML metadata.
- An instance of the SAP Cloud Identity Service needs to be created in your SAP BTP subaccount, including a parameter for SAP XSUAA cross-consumption. This will create a new application registration in your SAP IAS instance used for this scenario.
Feel free to check out the provided GitHub repo to find more details on these configuration steps. The cross-consumption parameter will result in access tokens issued by SAP IAS, which have the client ID of your SAP XSUAA application registration (created in SAP IAS during trust setup) in the audience parameter.
Together with the issuer being a trusted OIDC provider of SAP XSUAA (again based on your trust configuration), this will allow a token exchange using your SAP IAS token and retrieving an SAP XSUAA token for it. This feature is already supported by default when using the @sap/xssec package for token validation in version 3.2.0 or higher and happens automatically under the hood.
Hint – This scenario only works for SAP XSUAA service instances using the application plan (broker plan is not supported) and does not support exchanges for client credential tokens.
Follow the steps in the provided GitHub repo to learn how the PKCE flow can be implemented in your own SAP BTP subaccount and SAP Identity Authentication instance.
SAP AppGyver with OAuth 2.0 PKCE
Once you’ve successfully integrated your SAP Identity Authentication service with your SAP BTP subaccount you can create an SAP AppGyver application, which will handle the PKCE flow interactively. You can run a BTP Booster to configure your subaccount for Low-Code / No-Code Application Development and provision an SAP AppGyver tenant, as well as an instance SAP Business Application Studio. You can also run this example out of the SAP AppGyver Community Edition, if desired.
Following the step-by-step guide provided in the GitHub repo, will result in an app including: :
- An authentication page that contains a WebView component. The WebView URL is set to the authorization endpoint of your SAP IAS tenant. Configuration of the URL is based on the SAP Cloud Identity Services – Identity Authentication
- When users enter their credentials and click logon, an onLocationChange event is fired that lets us capture the authorization code returned by SAP IAS and store it in an application variable.
- An HTTP request that sends the required information to the token endpoint and adds the resultant access token, refresh token, id token, and expires in values to the same application variable. The access token can then be used in subsequent Bearer calls to secure OData resources provided by SAP BTP. We will demonstrate this by accessing a simple SAP CAP service to populate user data extracted from the access token.
Additionally, we will show you several advanced topics, including:
- A JS polyfill to enable SAP AppGyver to generate the SH256 encrypted code challenge and code verifier locally. You can find the reason why this needed here on the SAP AppGyver forums.
- How to set and read the refresh token from local device storage to minimize repetitive logons.
- A technique to locally evaluate the refresh token’s lifetime and prompt the user to login again when it expires.
You can follow along with the steps in GitHub. Furthermore, in the repository you can find the steps for implementing the traditional, client secret-based approach for anybody without an SAP IAS instance available.
We hope you like the sample and would love to hear from you in the comments. Let us know if this topic is of interest, and whether you have any use cases in mind for applying it.
*Please note – This use-case is currently being refactored as public SAP IAS tokens (issued by the PKCE flow) do not contain the required SAP XSUAA audience anymore. Therefore, an additional token exchange is required to run the end-to-end scenario.
So my question is, should we also consider applications running in a browser as a public client and always use authorization code with PKCE flow over authorization code if possible?
Also would be XSUAA support of PKCE on the roadmap?
btw, I do have a provisioned tenant of IAS on my free tier account with a default plan as subscription and an application plan as instance.
Regarding XSUAA support of PKCE, unfortunately I don't know if this support is in the roadmap. But you can find the roadmap for SAP Cloud Identity here: https://roadmaps.sap.com/board?SQ=INTG_IDN&PRODUCT=67837800100800007337&range=CURRENT-LAST
Thanks very much for the insight that you have an application plan instance of SAP Cloud Identity available in your BTP free-tier account. If you're interested in setting up this pattern (with or without AppGyver) feel free to reach out.
Hi James Rapp
Hi James Rapp
in your note you wrote:
Can you please elaborate, what further token exchange is required?
You can find some details in this excellent post (and it's predecessors):
It eventually comes down to the underlying CF spec:
just to add some quick thoughts on this. In case of Public Clients, an additional token exchange using Client Authentication is required by SAP IAS for security purposes. We've tested that exchange successfully in our sample application and hope that a standard implementation in the xssec library will be available soon.
In the meantime, feel free to implement the additional token exchange of the "Public" SAP IAS token to the "Private" SAP IAS token as you can see below. The assertion value is the actual "Public" token. Same also works using an X.509 certificate instead of a secret.
The token which you retrieve from that additional token exchange call, can be used to authenticate to your SAP XSUAA apps again.
Feel free to reach out in case of further questions!