cancel
Showing results for 
Search instead for 
Did you mean: 

Principal Propagation between different subaccounts on SAP BTP

renejurmann
Participant
0 Kudos

Hi experts,

we have following scenario:

  • An end user is accessing a Fiori app on a SAP BTP launchpad
  • The end user is triggering a save action within the app which sends an according POST request to a Node.JS backend application running on BTP as well.
  • The Node.JS application will execute a Database update to save the data entered by the end user in the DB

> as a result the field "modifiedBy" within the according DB table should be set to the user ID of the user who has authenticated at the launchpad

Remark: The UI should not explicitly send the user ID witih the POST request. Instead the already available authentication should be forwarded to the backend service.

Currently, the launchpad is running on a subaccount "Launchpad" and the backend service is running on a subaccount "DEV". Both subaccounts belong to the same global account.

The users who have access to the Launchpad subaccount are also available within the DEV subaccount. We are using the SAP default identity provider.

The Node.JS backend is a SAP CAP project connected to a HANA Shared DB. Within the Node.JS we have configured an xs-ssecurity.json as well as an approuter.

On the "Launchpad" subaccount we have created a destination using authentication OAuth2JWTBearer to connect to the backend service using client ID and secret as created by the xsuaa service. Using this configuration we can consume the REST APIs provided by the Node.JS service. Unfortunately, within the described scenario, not the current user is saved in DB but the client ID of the XSUAA service.

So, we tried to use different authentication types within the destination to check if the current users authentication token is forwarded to the DEV subaccount. We tried following:

  • OAuth2UserTokenExchange
  • OAuth2SAMLBearerAssertion

But for both we get an HTTP 500 within the UI when trying to access the backend service.

Can someone please tell us how to achieve the above given scenario?

We also think we might need some kind of trust between the two subaccounts but do not know how to establish this

Below some configurations we did so far.

Backend service - xs-security.json:

{
"xsappname": "dev-backend",
"tenant-mode": "dedicated",
"scopes": [],
"attributes": [],
"role-templates": []
}

Backend service - App router (xs-app.json):

{
"authenticationMethod":"route",
"routes": [
{
"source": "^/(.*)$",
"target": "$1",
"authenticationType": "xsuaa",
"destination": "srv_api",
"csrfProtection": false

}
]
}

Backend service - mta.yaml:

_schema-version: '3.1'
ID: DEV-BACKEND
version: 1.6.15
modules:
- name: DEV-BACKEND-srv
type: nodejs
path: gen/srv
requires:
- name: DB
- name: dev-backend-xsuaa
provides:
- name: srv_api
properties:
srv-url: '${default-url}'
parameters:
buildpack: nodejs_buildpack
- name: DB-deployer
type: hdb
path: gen/db
requires:
- name: DB
properties:
TARGET_CONTAINER: '~{hdi-service-name}'
- name: cross-container-service-1
group: SERVICE_REPLACEMENTS
properties:
key: ServiceName_1
service: '~{the-service-name}'
parameters:
buildpack: nodejs_buildpack
- name: dev-backend-approuter
type: approuter.nodejs
path: dev-backend-approuter
parameters:
disk-quota: 256M
memory: 256M
requires:
- name: dev-backend-xsuaa
- name: srv_api
group: destinations
properties:
forwardAuthToken: true
strictSSL: false
name: srv_api
url: '~{srv-url}'
resources:
- name: dev-backend-xsuaa
parameters:
path: ./xs-security.json
service: xsuaa
service-plan: application
service-keys:
- name: dev-backend-xsuaa-key
type: com.sap.xs.uaa
- name: DB
type: com.sap.xs.hdi-container
parameters:
service: hana
service-plan: hdi-shared
properties:
hdi-service-name: '${service-name}'
- name: cross-container-service-1
type: org.cloudfoundry.existing-service
parameters:
service-name: DB
properties:
the-service-name: '${service-name}'
parameters:
enable-parallel-deployments: true
build-parameters:
before-all:
- builder: custom
commands:
- npm install --production
- npx -p @sap/cds-dk cds build --production

Launchpad subaccount destination for OAuth2USerTokenExchange:

The URL is the URL of the application itself.

The token service URL is the URL as mentioned in the xsuaa service key including path "oauth/token".

Launchpad subaccount destination for OAuth2SAMLBearerAssertion:

Accepted Solutions (0)

Answers (4)

Answers (4)

CarlosRoggan
Product and Topic Expert
Product and Topic Expert

Hello,

maybe this blog post can help you

https://blogs.sap.com/2020/08/31/how-to-call-protected-app-from-external-app-from-different-subaccou...

It describes how to grant access across subaccounts.
It won't solve the 500 error, but maybe it gives you a hint on getting your scenario running.

Kind Regards,
Carlos

renejurmann
Participant
0 Kudos

Hi Carlos,

due to some other things we could not yet try to follow your blogs. I will come back as soon as we could test this.

Regards

René

renejurmann
Participant
0 Kudos

Hi all,

thanks for the feedback. But in the meantime we could convince the customer that it is easier to change the BTP subaccount structure. Now the Node.JS backend and SAPUI5 frontend run on the same BTP subaccount. So, no issues anymore as the Principal Propagation now works out of the box,,,

regardsRené
quovadis
Product and Topic Expert
Product and Topic Expert
0 Kudos

Hello Rene, I have published the following blog post where I explain the user propagation mechanism implemented with the IDP-initiated flow using the destination service trust as the Trusted IDP.

I hope that helps; kind regards; Piotr

renejurmann
Participant
0 Kudos

Hi Piotr,

I have checked your blog and tried this out but is not applicable for our scenario. On the one hand we do not have a "third party" application. And on the other hand if we would treat the SAPUI5 app as third party component we still have the fact that it is a SAPUI5 application and no backend application. Destination handling is a bit different for SAPUI5 apps and every test using "OAuth2SAMLBearerAssertion" destination has not worked (as also written in the inital question).

However, in the meantime we managed to convince the customer to simply change the BTP subaccount model. Now the Node.JS backend and SAPUI5 frontend run on the same BTP subaccount. So, no issues anymore as the Principal Propagation now works out of the box,,,

.

quovadis
Product and Topic Expert
Product and Topic Expert
0 Kudos

Hello renejurmann ,

Thx for your question. I have been working on a similar scenario for the last couple of days.

In order to solve the principal user propagation by calling into a backend service on "DEV" from an application on "Launchpad" but with the identity of the Launchpad user you may use the following documented scenario leveraging the OAuth2SAMLBearerAssertion where the destination service you created on Launchpad would become a trusted IDP on the subaccount DEV.

User Propagation between Cloud Foundry Applications page 158

User Propagation between Cloud Foundry Applications

In your case Launchpad is the application 1 and subaccount 1, 

DEV is the subaccount 2 and the backend service is the application 2.

Scenario
● You have deployed an application in a Cloud Foundry environment (application 1 or Launchpad).
● You want to call another Cloud Foundry application (application 2 or DEV) in a different 
subaccount, in the same or another region.
● You want to propagate the identity of the user that is logged in to application 1, to application 2.

Prerequisites
● You have two applications (application 1 and application 2) deployed in Cloud Foundry spaces in different
subaccounts in the same region or even in different regions.

● You have an instance of the Destination service bound to application 1.
● You have a user JWT (JSON Web Token) in application 1 where the call to application 2 is performed.

I hope that helps; kind regards; Piotr

PS. Out of my curiosity, xs-app.json points to a destination defined in the environment (in your mta.yaml deployment file); Have you tried to have the approuter use your OAuth2JWTBearer destination via a destination service or it was a manual test? I am asking because I cannot see in the mta.yaml any binding to the destination service.

renejurmann
Participant
0 Kudos

Hi Ptior,

unfortunately, this still does not work. I have established a trust relation ship between Launchpad and DEV subaccount as mentioned and updated the destination on the launchpad subaccount accordingly. But I still get the HTTP500 error.

The mta.yaml and xs-app.json as mentioned above are the once from the backend service on the DEV subaccount. Below you'll find the according files within the SAPUI5 running on the launchpad subaccount.

Additionally, SAPUI5 applications deployed to the BTP will not be created as apps on a cloud foundry space. Thus, the CLI command "cf env <app-name>" will not have effect for such apps. Additionally, token handling should be done via the bound destination service and key (which is visible via the BTP cockpit)

mta.yaml in SAPUI5 application

_schema-version: "3.2"
ID: launchpad-app
description: A Fiori application.
version: 0.4.0
modules:
- name: launchpad-app-dest-content
type: com.sap.application.content
requires:
- name: launchpad-app-dest-srv
parameters:
content-target: true
- name: launchpad-app-repo-host
parameters:
service-key:
name: launchpad-app-repo-host-key
- name: launchpad-app-uaa
parameters:
service-key:
name: launchpad-app-uaa-key
parameters:
content:
instance:
destinations:
- Name: launchpad-app_repo_host
ServiceInstanceName: launchpad-app-html5-srv
ServiceKeyName: launchpad-app-repo-host-key
sap.cloud.service: launchpad-app
- Authentication: OAuth2UserTokenExchange
Name: launchpad-app_uaa
ServiceInstanceName: launchpad-app-xsuaa-srv
ServiceKeyName: launchpad-app-uaa-key
sap.cloud.service: launchpad-app
existing_destinations_policy: ignore
build-parameters:
no-source: true
ignore: ["node_modules/"]
- name: launchpad-app-app-content
type: com.sap.application.content
path: .
requires:
- name: launchpad-app-repo-host
parameters:
content-target: true
build-parameters:
ignore: ["node_modules/"]
build-result: resources
requires:
- artifacts:
- launchpadapp.zip
name: launchpadapp
target-path: resources/
- name: launchpadapp
type: html5
path: .
build-parameters:
build-result: dist
builder: custom
commands:
- npm install
- npm run build:cf
supported-platforms: []
resources:
- name: launchpad-app-dest-srv
type: org.cloudfoundry.managed-service
parameters:
config:
HTML5Runtime_enabled: true
init_data:
instance:
destinations:
- Authentication: NoAuthentication
Name: ui5
ProxyType: Internet
Type: HTTP
URL: https://ui5.sap.com
existing_destinations_policy: update
version: 1.0.0
service: destination
service-name: launchpad-app-dest-srv
service-plan: lite
- name: launchpad-app-uaa
type: org.cloudfoundry.managed-service
parameters:
path: ./xs-security.json
service: xsuaa
service-name: launchpad-app-xsuaa-srv
service-plan: application
- name: launchpad-app-repo-host
type: org.cloudfoundry.managed-service
parameters:
service: html5-apps-repo
service-name: launchpad-app-html5-srv
service-plan: app-host
parameters:
deploy_mode: html5-repo
enable-parallel-deployments: true

xs-app.json in SAPUI5 application

{
"welcomeFile": "/index.html",
"authenticationMethod": "route",
"routes": [
{
"source": "^/v2/(.*)$",
"target": "/v2/$1",
"destination": "DEV_SUBACCOUNT",
"authenticationType": "xsuaa",
"csrfProtection": false
},
{
"source": "^/resources/(.*)$",
"target": "/resources/$1",
"authenticationType": "none",
"destination": "ui5"
},
{
"source": "^/test-resources/(.*)$",
"target": "/test-resources/$1",
"authenticationType": "none",
"destination": "ui5"
},
{
"source": "^(.*)$",
"target": "$1",
"service": "html5-apps-repo-rt",
"authenticationType": "xsuaa",
"scope": {
"GET": [
"$XSAPPNAME.Display"
],
"PATCH": [
"$XSAPPNAME.Display"
],
"POST": [
"$XSAPPNAME.Display"
],
"PUT": [
"$XSAPPNAME.Display"
],
"DELETE": [
"$XSAPPNAME.Display"
],
"default": [
"$XSAPPNAME.Display"
]
}
}
]
}
quovadis
Product and Topic Expert
Product and Topic Expert
0 Kudos

Hi Rene, Something got wrong with the yaml and json files formatting. Maybe you could try to edit the comment again?

From my end I am both able to call into a java backend service (that talks to a HANA Cloud HDI container) via a destination service destination from

1. either non-BTP web application by passing the bearer access token in the Authorization header of the backend service endpoint

You can test it from terminal using:

curl -H 'Authorization: Bearer <token>' <backend service URL endpoint>

2. or via approuter ; when routing through the destination service with an OAuth2SAMLBearerAssertion destination the route should be public as to avoid a redirect to the sub-account login page.

I hope that helps; Piotr

PS.

here goes a template of an OAuth2SAMLBearerAssertion destination definition:

{
  "Name": "destName",
  "Type": "HTTP",
  "URL": "<backend service URL>",
  "Authentication": "OAuth2SAMLBearerAssertion",
  "ProxyType": "Internet",
  "tokenServiceURLType": "Dedicated",
  "audience": "<audience>",
  "Description": "client_id/client_secret must be the backend service app router's xsuaa credentials",
  "authnContextClassRef": "urn:oasis:names:tc:SAML:2.0:ac:classes:PreviousSession",
  "tokenServiceUser": "<xsuaa client_id>",
  "HTML5.ForwardAuthToken": "false",
  "tokenServiceURL": "<tokenServiceURL>",
  "tokenServicePassword": "<tokenServicePassword>",
  "HTML5.DynamicDestination": "true",
  "clientKey": "<xsuaa client_id>",
  "nameIdFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
  "scope": "openid",
  "SystemUser": "<email>"
}
renejurmann
Participant
0 Kudos

Hi Piotr,

seems as if adding a code snippet is a bit buggy... However, I managed to edit the comment.

Additionally, our destination looks as follows:

regards

René

quovadis
Product and Topic Expert
Product and Topic Expert
0 Kudos

Hi Rene,

Thanks; The sapui5's mta.yaml looks fine. The approuter's xs-app.json looks good as well.

However, when I look at the DEV_SUBACCOUNT destination definition it does not have any scope attribute. That might be required.

May you please add the attribute "scope": "openid" ?

Could you also add the attribute "HTML5.ForwardAuthToken": "false" ?

When using OAuth2SAMLBearerAssertion the above property must be set to false! (just to be on the safe side).

(I also assume that in your xs-security.json you have the uaa.user scope defined.)

Having said that, I would encourage you to double check that the DEV_SUBACCOUNT destination you defined on SAPUI5 sub-account works correctly -- that means it can generate a bearer access token and that you can call the DEV sub-account micro-service endpoint using this generated bearer access token.

(This is what I have been doing to make sure it all works on my side.)

Another way of making sure your call is somehow reaching the DEV sub-account is to make sure your business user has logged into the Trusted IDP you created there using the destination service trust of the SAPUI5 sub-account.

You can see the user logon proof in the DEV cockpit under Security/User.

On a side note this trusted IDP should allow to create shadow users or otherwise you would have add your business user(s) into it manually.

I hope that helps;

kind regards

Piotr