This brief is to showcase how to use dynamic_dest routes with SAP Build Work Zone, standard edition service (a managed approuter) to run SAP BTP destinations defined on a BTP sub-account level.
Long story short. A community fellow Donny Xu had asked me the following question. Beyond the technical aspects involved, dynamic_dest routes offer a simple solution to the very conundrum of how to test [complex] destinations [w/o the principal user propagation] without writing a single line of code… I shall discuss: |
Putting it all together
SAP Build Work Zone, standard edition service (previously known as SAP Launchpad service) is the entry product to the SAP Build Work Zone product family. (It is also available as a free subscription service with SAP BTP Free Tier accounts.)
The dynamic_dest construct allows to call any destination defined on a sub-account level using the managed approuter, for instance:
https://<tenant>.launchpad.cfapps.<region>.hana.ondemand.com/dynamic_dest/<dest_name>/<api_endpoint>
Good to know:
- All types of destinations are supported.
- Please make sure all your dynamic_dest destinations have the HTML5.DynamicDestination magic property set to true.
You may refer to Configure Destinations guidelines from the official SAP documentation for further details.
Easy routing with OAuth2SAMLBearerAssertion destinations
I had a chance to test it using a number of OAuth2SAMLBearerAssertion sub-account level destinations.
You can call an OAuth2-protected remote system/API and propagate a user ID to the remote system by using the OAuth2SAMLBearerAssertion authentication type. The Destination service provides functionality for automatic token retrieval and caching, by automating the construction and sending of the SAML assertion. This simplifies application development, leaving you with only constructing the request to the remote system by providing the token, which is fetched for you by the Destination service. For more information, see User Propagation via SAML 2.0 Bearer Assertion Flow.
From the moment the target systems (SAP Analytics Cloud, SAP S/4HANA Cloud, SAP SuccessFactors etc) are correctly configured for the principal user propagation, the dynamic_dest should work out of the box…for instance:
SAP S/4HANA Cloud
https://<tenant>.launchpad.cfapps.<region>.hana.ondemand.com/dynamic_dest/S4HC-Product/$metadata
https://<tenant>.launchpad.cfapps.<region>.hana.ondemand.com/dynamic_dest/QUOVADIS-ISVENG-JWT/A_SalesOrder?$top=5
SAP SuccessFactors
https://ateam.launchpad.cfapps.sap.hana.ondemand.com/dynamic_dest/Quovadis-SAP-JWT/User?$top=1&$format=json
SAP Analytics Cloud
https://<tenant>.launchpad.cfapps.<region>.hana.ondemand.com/dynamic_dest/ateam-partnereng/stories
In all the above cases, the identity of an SAP BTP subaccount user that logs into the SAP Build Work Zone, standard edition (aka Launchpad service) is passed through to a dynamic destination. The destination service generates a SAML Assertion which is passed to the resource provider. In case a remote user login (with an IDP-delegated flow) is successful, a bearer access token is retrieved, and then passed to the business function call.
Thus the user’s identity must be known by a target system.
Easy routing with SAP BTP, Kyma runtime API rules
Nowadays, the kubernetes-managed, containerised workloads are becoming increasingly popular, almost ubiquitous.
With SAP BTP, Kyma runtime’s API rule CRD it is fairly easy to securely expose micro-services and their endpoints to the external world. As security is paramount, an API rule CRD can offer a number of access strategies to help restrict access to exposed endpoints. For the sake of this brief l opted to using the JWT access strategy as a way to protect an API rule endpoint(s) from unsolicited access. Good to know:
|
The most important part of the below API rule definition is the accessStrategies config as follows:
apiVersion: gateway.kyma-project.io/v1beta1
kind: APIRule
metadata:
name: "{{ .Values.services.srv.name }}-{{ .Release.Namespace }}"
spec:
gateway: {{ .Values.gateway }}
host: "{{ .Values.services.srv.name }}-{{ .Release.Namespace }}.{{ .Values.clusterDomain }}"
rules:
- accessStrategies:
- config:
jwks_urls:
- https://<tenant>.authentication.<region>.hana.ondemand.com/token_keys
trusted_issuers:
- https://<tenant>.authentication.<region>.hana.ondemand.com/oauth/token
required_scope:
- openid
handler: jwt
methods:
- GET
- PUT
- DELETE
- POST
- PATCH
- HEAD
path: /.*
service:
name: {{ .Values.services.srv.name }}
port: {{ .Values.services.srv.port }}
The tenant and the region values are those of the managed approuter’s subaccount.
Likewise, in order to further restrict access to the API rule to specific user categories only, it is possible to include additional user authorisations (scopes) that are assigned to a user JWT token via Roles and Roles Collections assigned to business users at a BTP sub-account level.
Kyma API rule as a business URL in a destination definition
Let’s assume the following dynamic destination definition:
It features two additional properties:
- HTML5.DynamicDestination: true — that makes it a dynamic destination
- HTML5.ForwardAuthToken: true — that tells the managed approuter to forward the business user JWT token to the API rule in the Authorization header as a bearer access token
Eventually, the above destination can be consumed as a dynamic destination as follows:
https://<tenant>.launchpad.cfapps.<region>.hana.ondemand.com/dynamic_dest/poster_dest/
This will result in calling of a Kyma API rule with the BTP user’s JWT passed in the Authorization header. Subsequently, the API rule will attempt to validate the endpoint access following the defined access strategies. After successful validation the access will be granted, otherwise a 401 error will be thrown.
Q. What if I wanted to use my own OIDC provider in this worklow, instead of relying on a forwarded user token?
A. That’s a very fair question. This is where OAuth2JWTBearer destination type can help exchange the user JWT token into an OAuth2 bearer access token with the required scopes…
To allow an application to call another application, passing the user context, and fetch resources, the caller application must pass an access token. In this authorization flow, the initial user token is passed to the OAuth server as input data. This process is performed automatically by the Destination service, which helps simplifying the application development: You only have to construct the right request to the target URL, by using the outcome (another access token) of the service-side automation.
Let’s take the above words for granted and build such a destination definition. We shall use an instance of XSUAA service acting as an OIDC provider…
And bingo! This works as advertised! (more details in the appendix section below….)
|
Conclusion
Calling a kyma-hosted micro-service from the comfort of a managed launchpad service with the integrated SAP BTP security has never been easier;
Calling into SAP LOB applications APIs has become a “piece of cake”….
All that with little to no code.
Last but not least, I hope you have enjoyed reading this blog post. Feel free to provide feedback in the comments section below.
Appendix
Making of OAuth2JWTBearer destination type
1a. the initial JWT user token…
{
"alg": "RS256",
"jku": "https://<tenant>.authentication.<region>.hana.ondemand.com/token_keys",
"kid": "<kid>",
"typ": "JWT",
"jid": "<jid>"
}.{
"jti": "<jti>",
"ext_attr": {
"enhancer": "XSUAA",
"subaccountid": "<subaccountid>",
"zdn": "<zdn>"
},
"xs.system.attributes": {
"xs.rolecollections": [
"Subaccount Service Administrator",
"SAP HANA Cloud Administrator",
"Subscription Management Dashboard Administrator",
"Subaccount Administrator",
"Launchpad_Admin",
"faas-faas_hc-faas"
]
},
"given_name": "Foo",
"xs.user.attributes": {},
"family_name": "Bar",
"sub": "<sub>",
"scope": [
"openid",
"faas-faas!t6455.Admin",
"faas-faas!t6455.User",
"uaa.user"
],
"client_id": "sb-faas-faas!t6455",
"cid": "sb-faas-faas!t6455",
"azp": "sb-faas-faas!t6455",
"grant_type": "authorization_code",
"user_id": "<user_id>",
"origin": "ldap",
"user_name": "foo.bar@sap.com",
"email": "foo.bat@sap.com",
"auth_time": <auth_time>,
"rev_sig": "<rev_sig>",
"iat": <iat>,
"exp": <exp>,
"iss": "https://<tenant>.authentication.<region>.hana.ondemand.com/oauth/token",
"zid": "<zid>",
"aud": [
"uaa",
"openid",
"faas-faas!t6455",
"sb-faas-faas!t6455"
]
}.[Signature]
1b … gets exchanged into a bearer access token. The bearer access token issuance endpoint is that of your custom OIDC provider (that can be an XSUAA service instance, SAP IAS OIDC application or any other third-party OIDC provider).
For the sake of simplicity the below destination definition snippet uses an instance of XSUAA service [on the same BTP sub-account as the managed approuter].
{
"owner": {
"SubaccountId": "<SubaccountId>",
"InstanceId": null
},
"destinationConfiguration": {
"Name": "quovadis-anywhere",
"Type": "HTTP",
"URL": "https://poster.<business_domain>.com/",
"Authentication": "OAuth2JWTBearer",
"ProxyType": "Internet",
"tokenServiceURLType": "Dedicated",
"HTML5.DynamicDestination": "true",
"clientId": "<clientId>",
"Description": "poster",
"scope": "openid",
"HTML5.Timeout": "60000",
"clientSecret": "<clientSecret>",
"tokenServiceURL": "https://<tenant>.authentication.<region>.hana.ondemand.com/oauth/token"
},
"authTokens": [
{
"type": "bearer",
"value": "eyJhbGciOiJSUzI1NiIsImprdSI6Imh0dHBzOi8vYXRlYW0uYXV0aGVudGljYXRpb24uc2FwLmhhbmEub25kZW1hbmQuY29tJN5W0TMNG32nVayH5yMwKHClhH3OW5Asx61KSE1hTu4HpwgDp4a-P-HG3Fw0XQmQIUe_m8YuQ0s6WFQHsbPlLLaINxfPL-Q",
"http_header": {
"key": "Authorization",
"value": "Bearer eyJhbGciOiJSUzI1NiIsImprdSI6Imh0dHBzOi8vYXRlYW0uYXV0aGVudGljYXRpb24uc2FwLmhhbmEub25kZW1hbmQuY29tLJN5W0TMNG32nVayH5yMwKHClhH3OW5Asx61KSE1hTu4HpwgDp4a-P-HG3Fw0XQmQIUe_m8YuQ0s6WFQHsbPlLLaINxfPL-Q"
},
"expires_in": "42389",
"scope": "openid"
}
]
}
2. Here goes the retrieved and decoded bearer access token:
{
"alg": "RS256",
"jku": "https://<tenant>.authentication.<region>.hana.ondemand.com/token_keys",
"kid": "<kid>",
"typ": "JWT",
"jid": "<jid>"
}.{
"jti": "<jti>",
"ext_attr": {
"enhancer": "XSUAA",
"subaccountid": "<subaccountid>",
"zdn": "<zdn>"
},
"xs.system.attributes": {
"xs.rolecollections": [
"Subaccount Service Administrator",
"SAP HANA Cloud Administrator",
"Subscription Management Dashboard Administrator",
"Subaccount Administrator",
"Launchpad_Admin",
"faas-faas_hc-faas"
]
},
"given_name": "Foo",
"xs.user.attributes": {},
"family_name": "Bar",
"sub": "<sub>",
"scope": [
"openid"
],
"client_id": "<client_id>",
"cid": "<cid>",
"azp": "<azp>",
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
"user_id": "<user_id>",
"origin": "ldap",
"user_name": "foo.bar@sap.com",
"email": "foo.bar@sap.com",
"rev_sig": "<rev_sig>",
"iat": <iat>,
"exp": <exp>,
"iss": "https://<tenant>.authentication.<region>.hana.ondemand.com/oauth/token",
"zid": "<zid>",
"aud": [
"openid",
"<client_id>"
]
}.[Signature]
that the managed approuter with the dynamic_dest route will use to call the business URL.
Troubleshooting tips
Sometimes it makes sense to smoke-test OAuth2SAMLBearerAssertion destinations with a valid user JWT token to be passed in the x-user-token header of the find destination REST API call.
This can be done using this handy jsp page on the subaccount level:
https://<tenant>.authentication.<region>.hana.ondemand.com/support.jsp
Then goto XS_APPLICATIONUSER
page so you can fetch a short-lived JWT token of the currently logged BTP user, as follows:
https://<tenant>.authentication.<region>.hana.ondemand.com/config?action=xs_appuser
XS_APPLICATIONUSER:
Type: JWT
Metadata: https://<tenant>.authentication.<region>.hana.ondemand.com/sap/trust/jwt
Test SQL:
-- start sql
SET 'XS_APPLICATIONUSER' = 'eyJhbGciOiJSUzI1NiIsImprdSI6Imh0dHBzOi8vYXRlYW0uYXV0aGVudGljYXRpb24uc2FwLmhhbmEub25kZW1hbmQuY29tFk5w-ug';
SELECT key,value from M_SESSION_CONTEXT WHERE KEY LIKE 'XS_%' AND CONNECTION_ID = CURRENT_CONNECTION;
-- end sql
Token Validity: 5 minutes
Details
SAP note: 2470084
Good to know:
- the BTP region for SAP BTP canary subaccounts is
sap
.
SAP Community: https://community.sap.com/
SAP Community Topic Page link: https://community.sap.com/topics/kyma
SAP Community Q&A Tags:
Kyma Open Source: https://answers.sap.com/tags/2936b97d-6a90-4cd8-b635-0e51441611eb
SAP BTP, Kyma runtime: https://answers.sap.com/tags/73554900100800003012
Follow me in SAP Community: Piotr Tesny