CAP Consuming Services - Reuse Service calling Destination with Client Credentials Flow fails
Describe the bug SAP internal CAP issue 17326
Scenario
- Reuse service is
B- Provides service
reuse-service-b - xsappname:
reuse-b!b400638 - client-id:
sb-reuse-b!b400638
- Provides service
- Consumer is
A- xsappnane:
a!t400638 - client-id:
sb-a!t400638 - Consumes service
reuse-service-b - Consumer service instance name
reuse-service-b-service-instance- uaa.xsappname of the clone of service-instance
2cffb32b-3ebf-44b1-ad60-88af2d3ff2a8!b400638|reuse-b!b400638 - uaa.client-id of the clone:
sb-2cffb32b-3ebf-44b1-ad60-88af2d3ff2a8!b400638|reuse-b!b400638
- uaa.xsappname of the clone of service-instance
- xsappnane:
service instance reuse-service-b-service-instance service binding credentials
{
"url": "https://reuse-service-b-srv.cert.cfapps.eu12.hana.ondemand.com/",
"uaa": {
"tenantmode": "shared",
"sburl": "https://internal-xsuaa.authentication.eu12.hana.ondemand.com",
"subaccountid": "SUB_ACCOUNT_ID",
"credential-type": "binding-secret",
"clientid": "sb-2cffb32b-3ebf-44b1-ad60-88af2d3ff2a8!b400638|reuse-service-b!b400638",
"xsappname": "2cffb32b-3ebf-44b1-ad60-88af2d3ff2a8!b400638|reuse-service-b!b400638",
"clientsecret": "67eafdfc-4563-4a52-ae85-b193c5133953$NPUmM9akxtuq5t8JO_Sw4A-QNOX_gCWpYQ3tkkif71A=",
"serviceInstanceId": "2cffb32b-3ebf-44b1-ad60-88af2d3ff2a8",
"url": "https://reference-data-access-lfci5awt.authentication.eu12.hana.ondemand.com",
"uaadomain": "authentication.eu12.hana.ondemand.com",
"verificationkey": "-----BEGIN PUBLIC KEY-----\nSECRET\n-----END PUBLIC KEY-----",
"apiurl": "https://api.authentication.eu12.hana.ondemand.com",
"identityzone": "PROV_SUB_DOMAIN",
"identityzoneid": "SUB_ACCOUNT_ID",
"tenantid": "SUB_ACCOUNT_ID",
"zoneid": "SUB_ACCOUNT_ID"
}
}
How to Test Scenario:
- get
Aclient-credentials JWT token using- pick client-id, client-secret from xsuaa binding of
A - you have
token-Anow
- pick client-id, client-secret from xsuaa binding of
- get Service B JWT Token using (Client-Credentials Token obtained for consumer-client-credentials-token)
- use
token-A - Pick
client-id,client-secret... fromBservice binding in ApplicationA - Use token exchange flow from
AtoB - you have
token-B - this is client credentials jwt token
- use
- Now, hit the application end point of reuse service
- use
token B- the client credentials token - the end-point is a CAP Odata consumer; internally calls remote OData service using destination using SAP Cloud SDK JS
- Application fails as SAP Cloud SDK JS is not able to resolve destination
- use
Callstack
getSubscriberToken (\home\vcap\app\node_modules\@sap-cloud-sdk\connectivity\dist\scp-cf\destination\get-subscriber-token.js:30)
DestinationFromServiceRetriever.getDestinationFromDestinationService (\home\vcap\app\node_modules\@sap-cloud-sdk\connectivity\dist\scp-cf\destination\destination-from-service.js:51)
getDestinationFromDestinationService (\home\vcap\app\node_modules\@sap-cloud-sdk\connectivity\dist\scp-cf\destination\destination-from-service.js:38)
getDestination (\home\vcap\app\node_modules\@sap-cloud-sdk\connectivity\dist\scp-cf\destination\destination-accessor.js:74)
process.processTicksAndRejections (<node_internals>/internal/process/task_queues:95)
await (Unknown Source:0)
processTicksAndRejections (<node_internals>/internal/process/task_queues:95)
await (Unknown Source:0)
useOrFetchDestination (\home\vcap\app\node_modules\@sap-cloud-sdk\connectivity\dist\scp-cf\destination\destination-accessor.js:38)
resolveDestination (\home\vcap\app\node_modules\@sap-cloud-sdk\connectivity\dist\scp-cf\destination\destination-accessor.js:51)
<anonymous> (\home\vcap\app\node_modules\@sap-cloud-sdk\http-client\dist\http-client.js:78)
executeHttpRequestWithOrigin (\home\vcap\app\node_modules\@sap-cloud-sdk\http-client\dist\http-client.js:278)
_executeHttpRequest (\home\vcap\app\node_modules\@sap\cds\libx\_runtime\remote\utils\client.js:64)
run (\home\vcap\app\node_modules\@sap\cds\libx\_runtime\remote\utils\client.js:227)
on_handler (\home\vcap\app\node_modules\@sap\cds\libx\_runtime\remote\Service.js:274)
next (\home\vcap\app\node_modules\@sap\cds\lib\srv\srv-dispatch.js:69)
handle (\home\vcap\app\node_modules\@sap\cds\lib\srv\srv-dispatch.js:73)
handle (\home\vcap\app\node_modules\@sap\cds\libx\_runtime\remote\Service.js:286)
dispatch (\home\vcap\app\node_modules\@sap\cds\lib\srv\srv-dispatch.js:35)
<anonymous> (\home\vcap\app\node_modules\@sap\cds\lib\srv\srv-dispatch.js:15)
run (\home\vcap\app\node_modules\@sap\cds\lib\srv\srv-api.js:72)
dispatch (\home\vcap\app\node_modules\@sap\cds\lib\srv\srv-dispatch.js:15)
run (\home\vcap\app\node_modules\@sap\cds\lib\srv\srv-api.js:76)
ODataClient.getTotalNumberOfRootRecords (\home\vcap\app\node_modules\@sap\my-common\util\ODataClient.js:392)
ODataClient.doSequentialInboundReplicationWithClientSidePagination (\home\vcap\app\node_modules\@sap\my-common\util\ODataClient.js:414)
processTicksAndRejections (<node_internals>/internal/process/task_queues:95)
await (Unknown Source:0)
ODataClient.doInboundReplication (\home\vcap\app\node_modules\@sap\my-common\util\ODataClient.js:678)
<anonymous> (\home\vcap\app\node_modules\@sap\my-common\srv\replicationRun\index.js:25)
next (\home\vcap\app\node_modules\@sap\cds\lib\srv\srv-dispatch.js:69)
handle (\home\vcap\app\node_modules\@sap\cds\lib\srv\srv-dispatch.js:73)
processTicksAndRejections (<node_internals>/internal/process/task_queues:95)
await (Unknown Source:0)
processTicksAndRejections (<node_internals>/internal/process/task_queues:95)
await (Unknown Source:0)
processTicksAndRejections (<node_internals>/internal/process/task_queues:95)
await (Unknown Source:0)
processTicksAndRejections (<node_internals>/internal/process/task_queues:95)
await (Unknown Source:0)
The runtime still tries to use user-jwt token and terminates;
The consumer call corresponds to consumer-A-clone-of-reuse-service-b-xsappname
In the runtime of service VCAP has xsappname of reuse-b
The cloud SDK is matching clone-xsappname of the clone against the environment variable xsappname
No match and call fails at retrieveUserTOken() below
/**
* @internal
*/
async function getSubscriberToken(options) {
const isXsuaaJwt = !!options.jwt && (0, jwt_1.isXsuaaToken)((0, jwt_1.decodeJwt)(options.jwt));
const userJwt = await retrieveUserToken(options, isXsuaaJwt);
const serviceJwt = await retrieveServiceToken(options, userJwt?.decoded);
return { userJwt, serviceJwt };
}
reuse service B xsuaa credentails in memory
xsuaa credentials from environment
[
{
"tenantmode": "shared",
"sburl": "https://internal-xsuaa.authentication.eu12.hana.ondemand.com",
"subaccountid": "SUB_ACCOUNT_ID",
"credential-type": "binding-secret",
"clientid": "sb-reuse-b!b400638",
"xsappname": "reuse-b!b400638",
"clientsecret": "SECRET",
"serviceInstanceId": "8efa7eca-938e-49f8-975e-458459376f18",
"url": "https://PROV_SUB_DOMAIN.authentication.eu12.hana.ondemand.com",
"uaadomain": "authentication.eu12.hana.ondemand.com",
"trustedclientidsuffix": "|reuse-b!b400638",
"verificationkey": "-----BEGIN PUBLIC KEY-----\\nSECRET\\nRQIDAQAB\\n-----END PUBLIC KEY-----",
"apiurl": "https://api.authentication.eu12.hana.ondemand.com",
"identityzone": "PROV_SUB_DOMAIN",
"identityzoneid": "SUB_ACCOUNT_ID",
"tenantid": "SUB_ACCOUNT_ID",
"zoneid": "SUB_ACCOUNT_ID"
}
]
incoming client-credentials token of the reuse service contains JWT clone of the service-instance of the reuse-service xsuaa client id
{
"jti": "b83ebb17d12c455598c9263927089b78",
"ext_attr": {
"enhancer": "XSUAA",
"subaccountid": "CONSUMER_SUB_ACCOUNT",
"zdn": "CONSUMER_SUB_DOMAIN",
"serviceinstanceid": "2cffb32b-3ebf-44b1-ad60-88af2d3ff2a8"
},
"sub": "sb-2cffb32b-3ebf-44b1-ad60-88af2d3ff2a8!b400638|reuse-b!b400638",
"authorities": [
"uaa.resource"
],
"scope": [
"uaa.resource"
],
"client_id": "sb-2cffb32b-3ebf-44b1-ad60-88af2d3ff2a8!b400638|reuse-b!b400638",
"cid": "sb-2cffb32b-3ebf-44b1-ad60-88af2d3ff2a8!b400638|reuse-b!b400638",
"azp": "sb-2cffb32b-3ebf-44b1-ad60-88af2d3ff2a8!b400638|reuse-b!b400638",
"grant_type": "client_credentials",
"rev_sig": "3270fa3b",
"iat": 1732515725,
"exp": 1732558925,
"iss": "https://PROV_SUB_DOMAIN.authentication.eu12.hana.ondemand.com/oauth/token",
"zid": "CONSUMER_TENANT_ID",
"aud": [
"uaa",
"sb-2cffb32b-3ebf-44b1-ad60-88af2d3ff2a8!b400638|reuse-b!b400638"
]
}
obviously there is no match of client-id of the jwt to the rda-service-srv bound xsuaa service instance's client-id getCredentialsWithJwt returns undefined
getServiceCredentials returns undefined
getXsuaaServiceCredentials throws error 'Could not find XSUAA service binding matching the token.'
A Reuse Service is called with the context of the Consumer Application (Clone of xsuaa-instance)
In this context, subscriber tenant level destination lookup within reuse service runtime always fails
Hi @KarthikeyanLoganathan ,
We still don't fully understand your issue here. Our guess would be that you are trying to get a destination registered in a provider subaccount from a subscriber context right? It feels like you are trying to manually fetch provider client credential and use that to get a provider destination from subscriber. In this case, we would recommend not doing so because subscriber should never have access to the provider destinations.
Instead, subscriber should delegate the call to provider and let provider call the destination or remote service. In subscriber context, subscriber can only get destinations from subscriber subaccount. If our understanding is correct, then please review your architecture for multi-tenancy.
REUSE SERVICE USE CASE IS FAILING IN CLOUD SDK JS
In reuse service scenario, we use service broker @sap/sbf, In my case I use xsuaa as credentials provider.
for my multi-tenant reuse Service B deployment, xsuaa credentilas are
- xsappname: reuse-b!b400638
- client-id: sb-reuse-b!b400638
- Uses service broker to expose the service
- A service instance gets clone of xsappname above like
2cffb32b-3ebf-44b1-ad60-88af2d3ff2a8!b400638|reuse-b!b400638
- A service instance gets clone of xsappname above like
My multi-tenant SaaS app A calls reuse service B
For my Multi-tenant SaaS App A deployment,
- xsappnane:
a!t400638 - client-id:
sb-a!t400638 - Has Service Instance of reuse Service B
- service binding as uaa section { } with credentilas
- xsappname:
2cffb32b-3ebf-44b1-ad60-88af2d3ff2a8!b400638|reuse-b!b400638 - client-id:
sb-2cffb32b-3ebf-44b1-ad60-88af2d3ff2a8!b400638|reuse-b!b400638 - When A calls --> B, JWT token is for the
- client id
sb-2cffb32b-3ebf-44b1-ad60-88af2d3ff2a8!b400638|reuse-b!b400638, - xsappname:
2cffb32b-3ebf-44b1-ad60-88af2d3ff2a8!b400638|reuse-b!b400638 - B runtime does not get the JWT token for
- xsappname
reuse-b!b400638 - client id
sb-reuse-b!b400638
- xsappname
- B only has xsuaa service binding for xsappname
reuse-b!b400638 - Cloud SDK has JWT token for
2cffb32b-3ebf-44b1-ad60-88af2d3ff2a8!b400638|reuse-b!b400638 -
Cloud SDK expects to lookup for
2cffb32b-3ebf-44b1-ad60-88af2d3ff2a8!b400638|reuse-b!b400638in VCAP_SERVICES whereas it only has xsappnamereuse-b!b400638in VCAP_SERVICES - HERE CLOUD SDK FAILS
- client id
refer to sap internal github issue for simplified example.
cap/issues/issues/17326
Have you fixed it already? I am facing that Issue for more than a year.
I found out that with those versions it is working: "@sap-cloud-sdk/connectivity": "3.15.0", "@sap-cloud-sdk/http-client": "3.15.0", "@sap/cds": "^8", "@sap/xssec": "4.2.1",
I also have opened an issue a while ago: https://github.com/SAP/cloud-sdk-js/issues/5688