Principal propagation in a multi-cloud solution between Microsoft Azure and SAP Business Technology Platform (BTP), Part I: Building the foundation
Note: This blog is the first part of a tutorial series. It describes principal propagation between an application frontend running on Microsoft Azure, calling a Web Service deployed to SAP BTP. Part II of this blog series extends the scenario by propagating the Azure-authenticated user via BTP and SAP Cloud Connector to an SAP Gateway system. Part III adds a business application context on top of the infrastructure based on a chatbot in Microsoft Teams, SAP BTP Integration and Core Data Services in the SAP Gateway system. A live demo of this scenario is also available on Episode #31 of the great SAP on Azure Video podcast series (starting at min 23:30) from Holger Bruchelt , Goran Condric, and Robert Boban. Part IV shows how to implement the chatbot from part III using Microsoft Power Virtual Agents, Power Automate and the on-premises data gateway. Finally, part V looks at different aspects of production readiness, such as API management, monitoring and alerting.
Start reading the blog series here, because you will learn about the core concepts and technologies used in the scenario, and configurations made in part I are reused in the other parts of this tutorial series. Enjoy!
Modern business applications running in the Cloud typically consist of many independent (micro)services. This architectural style enables them to rapidly respond to market conditions. However, such highly distributed systems also come with costs, e.g. for their increased communication overhead and additional operational complexity. Quite often, those services are not deployed in a single system landscape. They are distributed across different regions, geographies (e.g. for availability and performance reasons), or even clouds. In such a multi-cloud setup with multiple platforms from different vendors, interoperability and standards become an important aspect to consider when designing a solution. This is especially true for the security of the solution from an end-to-end perspective.
What does this mean? Let’s start with a simplified scenario of a business application that consists of a frontend component serving web pages to the end users accessing it via their web browser. The frontend also orchestrates the invocation of services in the backend implementing the core business logic. Those backend services may be co-hosted with the frontend component, but may also run on different cloud platforms. Users are authenticated at the frontend by entering their credentials into a login form, or the frontend may delegate this task to a third party, also known as an identity provider (IdP), enabling a single sign-on (SSO) experience to the user. The IdP can be a central user directory managed by the Cloud provider, your own (corporate) user store, or even a “social” IdP such as LinkedIn or Facebook. In all cases, the user is authenticated at the frontend, which then holds a valid session. When the frontend invokes a backend service, the service usually requires the user to authenticate before accessing any data in the backend. The authenticated user or “principal”) is a prerequisite to enforce any authorization rules in the backend. However, asking the user again for their credentials in the backend would be bad idea, both from a security and user experience perspective. Instead, the frontend should securely forward or propagate the principal to the backend to provide a secure and seamless experience. A few things need to be considered for this scenario to work:
- The authenticated user must be “serialized” by the frontend for the call to the backend service in a way that the backend can process it and authenticate the user from this serialized principal without the need to ask the user again for any credentials.
- The principal is transformed into a “security token” which should follow a standardized format. This ensures interoperability between the components of the solution when they are deployed on different platforms and clouds
- Exchanging highly security-sensitive information such as an authenticated user requires the security token to be protected against unauthorized access and modifications. Confidentiality and integrity must be ensured by the underlying security layers of the solution architecture.
- The components exchanging the principal must trust each other. The backend service(s) must only accept principal information sent by the frontend. Therefore, a trust relationship must be established by an administrator of the solution, usually by exchanging a digital certificate which identifies the component(s).
Interoperability and standards
To securely propagate the principal across different platforms and clouds, interoperability at the communication protocol and security token format level are key to the security architecture of the solution. A central standard addressing this scenario for principal propagation is described in RFC 7522, the Security Assertion Markup Language (SAML) 2.0 Profile for OAuth 2.0 Client Authentication and Authorization Grants. This extension to the OAuth 2.0 framework combines the interoperable and mature security token format from the SAML 2.0 standard with a simple request/response protocol from OAuth 2.0. Essentially, it describes how a client (in this scenario the web application frontend) accessing an OAuth-protected resource (the backend service) can obtain an OAuth access token from an OAuth authorization server by presenting a valid SAML assertion as the authorization grant. In this process, the user is not required to approve the token issuance at the authorization server. Other authorization grants of the OAuth 2.0 framework such as the authorization code ask for the user’s explicit permission to allow a client application to access resources on behalf of the user. Since we want to avoid any form of user action such as asking for credentials or user consent, RFC 7522 is the ideal candidate for this scenario.
Detailed scenario walk-through
Let’s take closer look at RFC 7522 in action in a concrete multi-cloud scenario of a web application that has a frontend deployed as an App Service in Microsoft Azure. Data is managed by a backend service running in the Cloud Foundry environment of SAP Cloud Platform, as shown in figure 1 below.
Figure 1: Multi-cloud scenario
The end-to-end process of a user authenticating at the frontend and accessing her data in the backend which performs authorization checks for the logged-in user follows these steps:
- The user (John Doe) accesses a protected resource of the frontend in the web application, which runs as an App Service instance in an Azure subscription. The web application’s frontend is registered in the subscription’s Azure Active Directory (Azure AD) tenant as an application to delegate authentication to the tenant.
- The web application redirects the user’s web browser to the Azure AD tenant’s OAuth authorization endpoint (https://login.microsoftonline.com/<tenant_id>/oauth2/v2.0/authorize) where the user enters their credentials and consent to the requested permissions or “scopes” of the web application.
- The frontend now has a session established for the user based on the information contained in the access token from Azure AD. This token cannot be used to authorize the call to the backend service in SAP Business Technology Platform (BTP). Services in BTP only accept access tokens issued by their trusted OAuth Authorization Server, the Extended Services for User Account and Authentication (XSUAA). The XSUAA service is an SAP-specific extension of Cloud Foundry’s standard UAA service. However, (XS)UAA supports the SAML Bearer Grant type according to RFC 7522, which allows the frontend in Microsoft Azure to request an OAuth access token from XSUAA in BTP with a SAML 2.0 assertion. Generating a valid SAML assertion in the frontend would result in a lot of development effort. Fortunately, Azure AD can provide a SAML assertion in exchange to an OAuth access token which it has issued to the authenticated user before. This feature is a non-standard extension to the On-Behalf-Of (OBO) flow in Azure AD and follows the general concept of a Security Token Service (STS) as specified by RFC 8693, OAuth 2.0 Token Exchange. When XSUAA receives a SAML assertion issued by an external IDP (here Azure AD) it has to pass the following validation steps:
- The correct
Audiencewhich is associated with the
Conditionelement of the SAML assertion. It defines under which security conditions the assertion is valid, such as the earliest and latest time instant before it expires, who can consume the assertion, etc. The
Audiencemust match the SAML service provider (SP) value (or
EntityID) of the XSUAA service instance in the BTP subaccount.
- The correct
Recipientwhich is associated with the
Subjectelement of the SAML assertion. It uniquely identifies the subject or user between the IDP (Azure AD) and SP (XSUAA) for whom the assertion has been issued, defines the format of the user identifier (e.g. an e-mail address). The
Recipientmust match XSUAA service instance endpoint (URL) where the assertion is received.
- A valid signature of a trusted IDP. The IDP (Azure AD) signs the SAML assertion to proof to the SP (XSUAA) that only it could have issued the assertion. In addition, the signature ensures the integrity of the assertion. Any changes of the assertion data while in transit over the public Internet, whether intentionally or unintentionally, would immediately invalidate the signature. To configure the above values correctly, the frontend app requests the SAML assertion for a second application registered in Azure AD, which represents the BTP subaccount. This app is created from the Azure AD SaaS app gallery as an enterprise app of type “SAP Cloud Platform”, which allows to configure SSO settings for SAML. Those settings are taken by Azure AD when it generates the SAML assertion in response to an OBO flow request. Details of the app registration and configuration are discussed below.
- The correct
- The frontend sends the SAML assertion to the token endpoint of the XSUAA service instance. This request to obtain a valid OAuth access token for the BTP backend service specifies the parameters as defined in section 2.1 of RFC 7522. Upon successful validation of the SAML assertion by XSUAA, a new access token is returned in the response. This access token is issued to the user originally identified by the
Subjectelement in the SAML assertion, and contains additional information required by the backend service to perform authorization checks.
- Finally, the frontend invokes the backend service in BTP with the XSUAA-issued access token sent in the authorization header of the request. The request is executed in the context of the user originally authenticated by the frontend application in Azure.
The next section shows the detailed configuration steps in Azure and BTP to setup the scenario.
Configuration of the scenario is done following these steps:
- Deployment of the backend service in BTP. We’ll use a simple servlet which prints out the logged-in (propagated) principal name and additional attributes
- Configuration of the role collection in the BTP subaccount, and export of the XSUAA SP SAML metadata for setting up the trust relationship
- Registration and configuration of the applications for the frontend and backend in Azure AD
- Setup of the trust relationship to Azure AD in the BTP subaccount
A final test with Postman will verify if the end-to-end process works.
Deployment of the backend service
Please make sure that your local environment meets the following prerequisites:
- Download and install the Cloud Foundry CLI
- Download and install the latest version of Maven
- Download and install a Java Development Kit (JDK) Version 8 or later
|1||Clone the repository with the sample code for the scenario using the web URL https://github.com/raepple/azure-scp-principal-propagation.git with your favourite IDE or command line tool.|
|2||Change to the
|3||Login to your BTP subaccount. In the following steps we’ll use a trial account on BTP with the command
|4||Create the XSUAA service instance for the backend service with the command
|5||Build the backend service with the command
|6||Edit the file manifest.yml and replace the string
|7||Deploy the backend service to your subaccount with the command
Configure authorizations for the backend service in BTP
Upon successful deployment of the backend service in BTP, the role and role collection settings for the service must be configured. The role collection is used later in the setup process to assign the permissions to the propagated principal.
|8||Open the BTP Cockpit in your browser and login with your account admin. Navigate to your trial account and select Security – Roles from the left side navigation menu. Click on the Add Using Same Role Template button create a new role.|
|9||Enter “Viewer” as the new role name and click Next. Continue with the configuration of the role attributes, which were defined by the xs-security.json “Viewer” role template. In our scenario, the Source of each attribute is the identity provider that originally authenticated the user, so Azure AD. Enter the following source attribute mappings:
|10||Leave the Role Collection selection empty for now, click Next and then Finish.The assignment to the role collection will be done in the next steps.|
|11||Select Security – Role Collections in the navigation menu, and click on New Role Collection|
Enter a name for the new Role Collection (e.g. “Application User”), click Save, and select the new Role Collection from the list.
Click on Edit and select the newly created Role “Viewer” from the “service” application in the dropdown list.
Confirm the changes with Save.
Go back to the subaccount level in the breadcrumb navigation and select Security – Trust Configuration from the navigation menu.
Click on SAML Metadata to export your (trial) subaccount’s service provider (SP) SAML metadata. Remember where the XML-file is saved – it is used later in Microsoft Azure AD to simplify the trust setup.
Finally, have look up a few configuration settings from the service binding between the “service” app and the XSUAA instance. These settings are required later to successfully request an OAuth access token from Azure with the Azure AD-generated SAML assertion (step 5 in Figure 1).
Select Spaces from the navigation menu and then your target (e.g. dev) space. In Services – Service Instances, select the “service-xsuaa” instance.
Select service in the Bound Applications table.
Click on Show sensitive data, and copy the values for the following binding properties, e.g. in a temporary text file:
App Registrations and Configuration in Azure AD
The following steps register the two applications in Azure AD:
- The first app registration for the frontend web app enables authentication for the user in Azure AD
- The second app registration represents the BTP subaccount with the backend service and is required to support the On-behalf-of flow to request the SAML assertion (step 4 in figure 1). This app will be registered as an enterprise application from the Azure AD Gallery
|16||Login to the Azure Portal and select Azure Active Directory from the Azure Services. Select App registriations from the left-hand navigation and click on New registration|
Enter an name for the new app registration of the frontend web app (e.g. “Contoso Web App”). Also provide a Redirect URI which is required for the authentication process. For the purpose of this tutorial, just enter a any URL string starting with https://
Click on Register.
Select Manage – Authentication and activate the checkbox for Access Tokens of the Implicit grant. This setting simplifies testing later in the setup process.
|19||Select Manage – Certificates & secrets from the navigation menu and click + New client secret. Enter a description of the new client secret for the web app frontend registration, choose an expiration time, and click Add.|
|20||Copy the newly generated secret in a text file for later use.|
|21||Select Manage – Expose an API from the navigation menu and click + Add a scope. You’ll be asked to set the Application ID URI for the frontend app registration. Accept the proposed default value by clicking Save and continue.|
|22||Choose a name of the new scope (e.g. “scp.access”) and allow Admins and users to consent. Enter the required display name and consent description, and click Add scope.|
|23||Finally, copy the Application (client) ID of the new web app frontend application registration from the Overview page to a text file. This will be required later for configuration and testing purposes.|
|24||Next, register the second app in Azure AD which represents the backend service in BTP for which the SAML assertion will be requested (step 4 in figure 1). Select Manage – Enterprise Applications from the Azure AD service navigation menu, and click + New application.|
|25||Enter “SAP Cloud Platform” in the search box, and select the tile for “SAP Cloud Platform” in the search results. Next, enter a name for the new enterprise app, e.g. “SAP Cloud Platform <your trial account name>”. Click Create.|
|26||For the newly created Enterprise Application registration, select the tile “2. Set up single sign on” and select the method “SAML“|
|27||Click Upload metadata file and select the file containing your BTP subaccount’s SAML Service Provider metadata you’ve download in step 13. Click Add.|
Most of the fields in the Basic SAML Configuration are automatically populated by the uploaded metadata file, which greatly simplifies the trust setup between Azure AD and BTP.
For the mandatory Sign on URL, enter the value you copied from the url field of the XSUAA service instance binding infomration in step 15. Do not save the settings yet!
Next, change the Reply URL. The already existing value was taken from the SAML metadata file and is the Assertion Consumer Service URL for the SAML protocol response message of your BTP tenant-specific XSUAA service instance,
However, for RFC 7522, the SAML assertion is sent to the OAuth token endpoint of your XSUAA service instance, which is almost the same URL as for the SAML protocol. Just replace “saml/SSO” with “oauth/token” in the existing URL.
This ensures that the Recipient URL is set correctly in the generates SAML assertion by Azure AD.
Next, you will configure additional attributes of the user which will be federated between Azure AD and BTP.
Click on the pencil symbol for User Attributes and Claims
Select the Unique User Identifier (Name ID) from list and change the Source attribute to user.mail.
This will use the user’s email address in Azure AD as the login name (Name ID) of the subject in the generated SAML assertion. Using the email address is a best practice for a common user identifier when federating users across clouds and other platforms.
To demonstrate federation of user attributes in the scenario, click + Add new claim and enter country in the name field.
Set http://schemas.xmlsoap.org/ws/2005/05/identity/claims as the Namespace.
Select user.country for the Source attribute.
Navigate back to the Setup Single Sign-On with SAML settings and scroll down to the SAML Signing Certificate settings.
Click the Download link for the Federation Metadata XML. The downloaded file will be used later to setup the trust relationship in BTP.
Note: If there is no Download link yet, try to refresh the page.
|34||Navigate back to the App Registrations in the Azure AD service navigation menu. Select All Applications as the filter settings from the top of the list and select the application registered for the backend service in BTP (e.g. “SAP BTP <name of your trial account>”).|
|35||Select Manage – Expose an API from the navigation menu, and click + Add a client application|
Enter the client ID copied in step 23 into the corresponding field. Also activate the checkbox for the Authorized scopes. This will authorize the web app frontend application to request a SAML assertion from Azure AD for the backend service application.
Confirm with Add application.
Configure trust relationship to Azure AD in BTP
The trust relationship for SAML is bi-directional. In this step, the exported metadata file from Azure AD will be imported into the BTP subaccount to finalize the trust setup.
|37||In BTP Cockpit, navigate back to the (trial) subaccount level via the breadcromb naviagtion on the top, select Security – Trust Configuration from the navigation menu, and click New Trust Configuration|
Upload the metadata file you exported in step 33 and click on Parse. Enter a name for the new trust configuration (e.g. “Contoso Azure AD”) and deactivate the checkbox Available for User Logon, because this IDP is only trusted for non-interactive backchannel SSO, and not for normal user login/SSO.
Configure the authorizations of a user you’ll use for testing.
Select the Security – Role Collections from the navigation menu. Look for the Role Collection “Application User” create in step 12 in the list. You can also filter the list by entering the Role Collection’s name in the search box on the upper right corner.
Click on the Role Collection Application User in the list.
Select the tab “Users (0)”. There are no users assigned to the role collection yet.
In the ID and E-Mail entry fields, enter the e-mail address of the test user in your Azure AD tenant that you will use in the next step for testing.
In the Identity Provider dropdown list, select the previously created Identity Provider for your Azure AD tenant.
Click the “+” button to add the user.
Testing the scenario
After completing the setup in Azure AD and BTP, let’s verify if the principal propagation works correctly. For testing purposes, we’ll use Postman to simulate the frontend web app. At a later point it is planned to enhance this blog and the associated GitHub repository with a code sample for a web application in Azure which essentially performs the same requests.
Download, install and start Postman, and execute the following steps:
|42||Click Import, select Upload File, and select the Postman Collection file Azure AD SCP Principal Propagation.postman_collection.json from the Postman folder in the GitHub repository. Then click Import.|
|43||Open the context menu for the imported Collection (…) and select Edit|
|44||Switch to the Variables tab and enter the values for the placeholders:
Click Reset All to update the current values.
|45||Open the collection, select the first request, and click Send.|
|46||Switch to the Console view in Postman, and copy the complete request URL|
|47||Paste the URL in a Web Browser and send the request again. Login with your test user credentials in Azure AD. The browser will be redirected to a potentially unknown reply URL (e.g. https://webapp.contoso.com/auth). Copy the request parameter access_token from the URL.|
|48||You may want to decode and inspect the access_token with a tool of your choice, e,g, jwt.io|
Open the second request in the Postman collection, switch to the Body tab of the request, and paste the value of the access token. This requests the SAML assertion for the Azure AD-authenticated user.
|50||Copy the access_token value from the response. This is the SAML assertion generated by Azure AD. To inspect the content of the SAML assertion, Base64-decode it with an (online) tool of your choice.|
|51||Open the third request in the Postman collection, switch to the Body tab of the request, and paste the assertion as the value for the assertion key.|
Switch to the Authorization tab. The request will use Authentication Type Basic. Username is the clientid and password the clientsecret you obtained from the XSUAA service binding in step 15.
|53||From the XSUAA response copy the access_token element. Again, you may want to inspect the token with a tool of your choice.|
Open the last request in the collection. This request calls the backend service in BTP and requires the access token from the above request. Select the Authorization tab and paste the access_token value into the Access Token field.
|55||The response from BTP shows the propagated principal’s name identifier and the additional user attributes which were successfully federated. The principal also obtained the required scopes (“Display”) in BTP to call the backend service based on the Role Collection assignment.|
Congratulation on completing part I of the scenario! Ready to take the next challenge and propagate the user even further to your SAP systems on-premise? Then go on reading part II of this blog series.