cancel
Showing results for 
Search instead for 
Did you mean: 

Multitenancy with cloud foundry destination service

joachimvanpraet
Active Participant

Hi all,

I was able to deploy a multitenancy application on cloud foundry.
In a nodejs app I can show the current user and tenant ID.
Next step is to share my destination service in my provider account to be able to read my destinations in my consumer accounts.

I'll give you an overview of my subaccounts:

1. Provider tenant: On this account I deployed the multi tenant app, I registered it in the saas service. There is a destination service linked to the approuter and to the destination service.
2. Consumer tenant 1: I subscribed to the app in the provider tenant and I'm able to open the application.


3. Consumer tenant 2: I subscribed to the app in the provider tenant and I'm able to open the application.


I use the subscription middleware of the approuter for the implementation of the callback entries.
When I check my subscriptions, I can see they are successfull and the destination service is shared.

now I created 3 destinations called SEARCHENGINE

for the provider tenant:

for the consumer tenant 1:

and for the consumer tenant 2:

In my nodejs app on the provider tenant, I want to be able to read the destination configurations on the consumer tenants.

Therefore I created a new route in the application.

router.get('/dest/:destinationName', async function(req, res, next) {
  try{
    const destinationName = req.params.destinationName;
    const destination = readDestination(destinationName);
    res.status(200).json(destination);
  } catch (error) {
    console.error("Error!");
    console.error(error);
    console.error("End Error!");
    res.status(400).send(error);
  }
});

the readDestination function is reading the environment variables, takes the destination service credentials and calls the destination service api.

unfourtunatly, the result of https://<url-consumer-tenant-1>/srv/dest/SEARCHENGINE and https://<url-consumer-tenant-2>/srv/dest/SEARCHENGINE is the same. They return the destination configuration of the Provider tenant:

{
  "owner": {
    "SubaccountId": "<subaccountId-provider tenant>",
    "InstanceId": null
  },
  "destinationConfiguration": {
    "Name": "SEARCHENGINE",
    "Type": "HTTP",
    "URL": "https://default.com",
    "Authentication": "NoAuthentication",
    "ProxyType": "Internet"
  }
}

When I remove the destination in the provider account, I get a 404 error from the destination service.

Unsubscribing and resubscribe does not help.

this is the declaration of the destination service in the mta:

#DESTINATION
 - name: saas_dest_service
   type: destination
   parameters:
      service-plan: lite
      shared: true

my environment variables for the nodejs app:

  "destination": [
   {
    "binding_name": null,
    "credentials": {
     "clientid": "sb-clone********!b20897|destination-xsappname!b404",
     "clientsecret": "********=",
     "identityzone": "testdpi",
     "instanceid": "a83ade9d-8c3b-4a1f-a0d8-f8e0bcf35858",
     "tenantid": "<subaccountId-provider tenant>",
     "tenantmode": "dedicated",
     "uaadomain": "authentication.eu10.hana.ondemand.com",
     "uri": "https://destination-configuration.cfapps.eu10.hana.ondemand.com",
     "url": "https://testdpi.authentication.eu10.hana.ondemand.com",
     "verificationkey": "-****-",
     "xsappname": "clone*******!b20897|destination-xsappname!b404"
    },
    "instance_name": "saas_dest_service",
    "label": "destination",
    "name": "saas_dest_service",
    "plan": "lite",
    "provider": null,
    "syslog_drain_url": null,
    "tags": [
     "destination",
     "conn",
     "connsvc"
    ],
    "volume_mounts": []
   }
  ],

As you can see, the destination service's tenantmode is dedicated. Is this correct?

How can I make this tenant-aware?

I think I have to add the current tenantId somewhere in the request of my destination service call, but I don't know where.

kr,

Joachim

Accepted Solutions (1)

Accepted Solutions (1)

joachimvanpraet
Active Participant

Problem solved!

To make the service tenant aware you need to request the oauth token to a different url.
I was requesting the token on

`${destination.credentials.url}/oauth/token`

this wil search the destinations on the provider side.

If you wish to find the destinations on the consumer side, you need to construct another token url.

`https://${req.authInfo.subdomain}.${destination.credentials.uaadomain}/oauth/token`

you can use the same client id and secret. when you use this token to query the destination service, everything works as expected.

gregorw
Active Contributor
0 Kudos

Hi Joachim,

did you have to make any other special configuration? I've tried to request the token from the subscriber authentication endpoint as you've described and as it is also documented at Consume a Subscription-Level Destination. But my request fails with:

{
  "error": "unauthorized",
  "error_description": "Bad credentials"
}

when I try the same against my provider authentication endpoint I receive a token.

Best Regards
Gregor

gregorw
Active Contributor

Hi Joachim,

with the help of Register the Multitenant Application to the SAP SaaS Provisioning Service I understood that I need to provide the generated xsappname as a dependency that is returned when /callback/v1.0/dependencies is called. In a CAP application that is described in SAP BTP Dependencies. But adding such a generated name into the package.json isn't the correct way. So I've added this lines to my server.js:

const xsenv = require("@sap/xsenv");
xsenv.loadEnv();
const services = xsenv.getServices({
  dest: { tag: "destination" },
});

cds.env.mtx.dependencies = [services.dest.xsappname];

that way I can set the dependency dynamically.

Best Regards
Gregor

Answers (0)