Multitenancy – Develop and Register Multitenant Application to the SAP SaaS Provisioning Service in Cloud Foundry

On the SAP BTP, you can develop and run multitenant applications, and share them with multiple consumers simultaneously in either Cloud Foundry or Kyma runtime.

In this post, you can get how many steps it takes to do from a normal application to a multitenant application in general. And more posts will follow and provides more practical demos on Cloud Foundry and Kyma runtime as well as local troubleshooting. Let’s stay tuned.

Prerequisites

Business applications. For more information on how to develop and run business applications on SAP Business Technology Platform (SAP BTP) using our cloud application programming model, APIs, services, tools, and capabilities, see Development on BTP.

BTP account. Here are some channels prepared for you to get BTP accounts:

What is Multitenancy?

SAP BTP provides a multitenant functionality that allows application providers to own, deploy, and operate tenant-aware applications for multiple consumers, with reduced costs.

With tenant-aware applications, you can:

  • Separate data securely for each tenant

  • Save resources by sharing them among tenants

  • Update applications efficiently, in a single step

How Does Multitenancy Work for the Application Consumer?

For a consumer to use a tenant-aware application on SAP BTP, the application owner must ensure that each consumer:

  1. Has a dedicated subaccount in the application provider’s global account.

  2. Subscribes to the application using either the SAP BTP cockpit, SAP BTP command-line interface, or a dedicated REST API.

    A subscription means that there is a direct relationship between an application provider and the consumer’s tenant. The application provider authorizes the consumer tenant to use the application.

  3. Receives a dedicated URL so that its business users can access the application

    As with any application running in SAP BTP, these multitenant applications consume platform resources, such as compute units, structured and unstructured storage, and outgoing bandwidth. The costs for these consumed resources, and those of the application consumer, are billed to the provider of the multitenant application.

Workflow

Implement Subscription callbacks API

To perform internal tenant onboarding activities, such as creating a database schema for tenants, you must implement the Subscription callbacks of the SAP Software-as-a-Service Provisioning service (saas-registry) and use the information provided in the subscription event. You can also implement the getDependencies callback to obtain the dependencies of any SAP reuse services by your application.

When a consumer tenant creates or revokes a subscription to your multitenant application via the cockpit, the SAP SaaS Provisioning service calls the application with two Subscription callbacks. The first callback gets the dependencies of any reuse services used by your application. The second callback informs the application about the subscription event (PUT for subscribe and DELETE for unsubscribe) and provides details about the subscriber (the consumer tenant).

  • PUTcallback/v1.0/tenants/*

    To inform the application that a new consumer tenant wants to subscribe to the application, implement the callback service with the PUT method. The callback must return a 200 response code and a string in the body, which is the access URL of the tenant to the application subscription (the URL contains the subdomain). This URL is generated by the multitenant application based on the approuter configuration (see Configure the approuter Application)

    https://trial-tia-mt-hw-ar-app.exercise.sap-samples.cn40.apps.platform.sapcloud.cn

    The URL must be in the format: https://<SUBSCRIBER_TENANT_SUBDOMAIN >-<APPROUTER_APPLICATION_HOST>.<CF_DOMAIN>

  • DELETE – ‘callback/v1.0/tenants/*’

    To inform the application that a tenant has revoked its subscription to the multitenant application and is no longer allowed to use it, implement the same callback with the DELETE method.

    Payload of subscription PUT and DELETE methods:

    {
       "subscriptionAppId": "<value>",                     # The application ID of the main subscribed application.
                                                           # Generated by Authorization and Trust Management service(XSUAA)-based on the xsappname.
       "subscriptionAppName": "<value>"                   # The application name of the main subscribed application.
       "subscribedTenantId": "<value>"                     # ID of the subscription tenant
       "subscribedSubdomain": "<value>"                   # The subdomain of the subscription tenant (hostname for 
                                                           # the identityzone).
       "globalAccountGUID": "<value>"                     # ID of the global account
       "subscribedLicenseType": "<value>"                 # The license type of the subscription tenant
    
    }
  • GET – ‘callback/v1.0/dependencies’

    [ 
       {
         "xsappname" : "<value>"   # The xsappname of the reuse service which the application consumes.
                                   # Can be found in the environment variables of the application:
                                   # VCAP_SERVICES.<service>.credentials.xsappname
       }
    ]

    The callback must return a 200 response code and a JSON file with the dependent services appName and appId, or just the xsappname.

    The JSON response of the callback must be encoded as either UTF8, UTF16, or UTF32, otherwise an error is returned.

Create and Configure the Approuter Application

When a consumer accesses the application, their consumer tenant calls the multitenant application via the application router with their tenant-specific URL. The application router then derives the tenant from the URL and calls the tenant-aware XSUAA (containing the user account and authentication service), which is responsible for the authentication of the business user. The XSUAA reads the tenant and gets the customer-specific identity provider (IdP) from the tenant-specific identity zone. Then, the XSUAA delegates the authentication to the configured IdP, and creates a JSON Web Token (JWT) that contains the tenant, the current user, and the authorization scopes of the user. The JWT is then sent back to the application router, and from there to the application.

  1. Create the application structure of the application router and configure it accordingly.

    For general instructions, see Application Router.

  2. Configure the application router with the destination of your multitenant application.

    If you are defining the destination as an environment variable for the approuter application, set the router information in the env: destinations section of the manifest.yml.

    For example:

    ---
    applications:
    
    - name: approuter-saas-app
     host: approuter_saas_app
     path: approuter
     buildpack: nodejs_buildpack
     memory: 256M
     env:
       TENANT_HOST_PATTERN: "^(.*).<mydomain.com>
       destinations: >
         [
           {"name":"saas-application",
             "url":"https://<backend-saas-app.mydomain.com>",
             "forwardAuthToken": true}
         ]

    For more information, see Application Routes and Destinations.

  3. Configure the routes in the application router security descriptor file (xs-app.json) so that application requests are forwarded to the multitenant application destination.

    For example:

    {
       "routes": [{
           "source": "/",
           "target": "/",
           "destination": "saas-application"
       }]
    }

    For more information, see Routing Configuration File.

  4. Use the push command in the cf CLI to deploy the approuter application to the Cloud Foundry space where your multitenant is deployed.

    cf push

Create and Configure Authentication and Authorization with XSUAA

  1. Configure XSUAA instance.

    In Cloud Foundry, create a security descriptor file xs-security.json in JSON format that specifies the functional authorization scopes for the application, For general instructions, see SAP Authorization and Trust Management Service in the Cloud Foundry Environment:

    • Define the application provider tenant as a shared tenant:

      "tenant-mode":"shared"
    • Provide access to the SAP SaaS Provisioning service SAP Authorization and Trust Management service (technical name: saas-registry) for calling callbacks and getting the dependencies API by granting scopes:

      "grant-as-authority-to-apps" : [ "$XSAPPNAME(application,sap-provisioning,tenant-onboarding)"]

    For example:

    {  
       "xsappname":"saas-app",
       "tenant-mode":"shared",
       "scopes":[  
         {  
             "name":"$XSAPPNAME.Callback",
             "description":"With this scope set, the callbacks for subscribe, unsubscribe and getDependencies can be called.",
             "grant-as-authority-to-apps":[  
               "$XSAPPNAME(application,sap-provisioning,tenant-onboarding)"
             ]
         }
         ....
       ],
       ....
    }
  2. Create an XSUAA instance with the configuration above.

    In the Cloud Foundry space in the provider’s subaccount where your application is going to be deployed (see Deploy the Multitenant Application to the Provider Subaccount), create an XSUAA service instance with the security configurations, which you defined in the security descriptor file in the previous step, by executing this command in the Cloud Foundry command line interface (cf CLI):

    cf create-service xsuaa application <XSUAA_INSTANCE_NAME> -c <XS_SECURITY_JSON>
  1. Bind the XSUAA Service Instance to the Multitenant Application and Approuter Application

    In the Cloud Foundry, execute the following commands command line interface to bind both your multitenant application and the approuter application to the XSUAA service instance.

    cf bind-service <APP_NAME> <XSUAA_INSTANCE_NAME>
    cf bind-service <APPROUTER_APP_NAME> <XSUAA_INSTANCE_NAME>

Register the Multitenant Application to the SAP SaaS Provisioning Service

  1. Create a service instance of the SAP SaaS Provisioning service with the configuration below:

    In the Cloud Foundry, we can create instance by executing this command:

    cf create-service saas-registry application <SAAS_REGISTRY_SERVICE_INSTANCE> -c <JSON_CONFIG_FILE>

    The configuration JSON file must follow the following format and set these properties (note that instead of xsappname you can use appId):

    { 
       "xsappname" : "<xsappname>",
       "appUrls": {
         "getDependencies" : "<getDependenciesUrl>", 
         "onSubscription" : "<onSubscriptionUrl>/{tenantId}"
       },
       "displayName" : "<displayName>",
       "description" : "<description>",
       "category" : "<category>",
       "onSubscriptionAsync": true/false,
       "callbackTimeoutMillis": <int>
       "allowContextUpdates": true/false,
    }

    Specify the following parameters:

    Parameters Description
    xsappname The xsappname configured in the security descriptor file used to create the XSUAA instance (see Develop the Multitenant Application).
    getDependencies (Optional) Any URL that the application exposes for GET dependencies. If the application doesn’t have dependencies and the callback isn’t implemented, it shouldn’t be declared.NoteThe JSON response of the callback must be encoded as either UTF8, UTF16, or UTF32, otherwise an error is returned.
    onSubscription Any URL that the application exposes via PUT and DELETE subscription. It must end with /{tenantId}. The tenant for the subscription is passed to this callback as a path parameter. You must keep {tenantId} as a parameter in the URL so that it’s replaced at real time with the tenant calling the subscription. This callback URL is called when a subscription between a multitenant application and a consumer tenant is created (PUT) and when the subscription is removed (DELETE).
    displayName (Optional) The display name of the application when viewed in the cockpit. For example, in the application’s tile. If left empty, takes the application’s technical name.
    description (Optional) The description of the application when viewed in the cockpit. For example, in the application’s tile. If left empty, takes the application’s display name.
    category (Optional) The category to which the application is grouped in the Subscriptions page in the cockpit. If left empty, gets assigned to the default category.
    onSubscriptionAsync Whether the subscription callback is asynchronous.If set to true, callbackTimeoutMillis is mandatory.
    callbackTimeoutMillis The number of milliseconds the SAP SaaS Provisioning service waits for the application’s subscription asynchronous callback to execute, before it changes the subscription status to FAILED.
    allowContextUpdates Whether to send updates about the changes in contextual data for the service instance.For example, when a subaccount with which the instance is associated is moved to a different global account.Defaut value is false.

    For example:

    {
       "xsappname":"saas-app",
       "appUrls": {
         "getDependencies" : "https://${org}-mt-hw-node-app.exercise.sap-samples.cn40.apps.platform.sapcloud.cn/callback/v1.0/dependencies",
         "onSubscription" : "https://${org}-mt-hw-node-app.exercise.sap-samples.cn40.apps.platform.sapcloud.cn/callback/v1.0/tenants/{tenantId}"
       },
       "displayName" : "Hello World",
       "description" : "My multitenant biz app",
       "category" : "Test"
    }
  2. Bind the SAP SaaS Provisioning service instance to the multitenant application that you deployed in your runtime

    In the Cloud Foundry, we can create instance by executing this command:

    cf bind-service <APP_NAME> <SAAS_REGISTRY_SERVICE_INSTANCE>

    To ensure that the environment variables in the application take effect, execute the following cf CLI command:

    cf restage <APP_NAME>

[Optional] Using SAP SaaS Provisioning Service APIs to Manage Multitenant Applications

Get the URL of your SaaS Provisioning Service Instance. For example:

https://saas-manager.cfapps.cn40.platform.sapcloud.cn

Get the Access Token of your SaaS Provisioning Service Instance with its Client ID and Client Secret, for more details: Getting an Application Access Token.

Method URL Name
GET <URL>/saas-manager/v1/application Get application details
POST <URL>/saas-manager/v1/application/tenants/<tenantId>/subscriptions Subscribe the tenant to an application
DELETE <URL>/saas-manager/v1/application/tenants/<tenantId>/subscriptions Unsubscribe the tenant from an application
GET <URL>/saas-manager/v1/application/subscriptions Get application subscriptions
PATCH <URL>/saas-manager/v1/application/tenants/<tenantId>/subscriptions Update subscription dependencies
GET <URL>/api/v2.0/jobs/<jobId> Get subscription job information Note: This API checks the status of the job created after one of the following APIs has been called:Subscribe the tenant to an applicationUnsubscribe the tenant from an applicationUpdate subscription dependencies
PATCH <URL>/saas-manager/v1/applications/<appName>/subscription Update subscription plan Note: You can update a subscription plan only if additional plans for the application are entitled to the subaccount you’re using and if your subscription is eligible for a plan update.

[Optional] Using the Subscription Management Dashboard

  1. Assign Roles

    To access the subscription management dashboard, you must be assigned to one of these roles:

    • Subscription Management Dashboard Administrator – Provides access to the dashboard with full read and write capabilities.

    • Subscription Management Dashboard Viewer – Provides read-only access to the dashboard.

    Add the relevant role to a role collection and assign yourself or another user to it.

    1. In the Instances section, find the instance of the service (technical name: saas-registry) with the application plan and click on it.

      Details about the instance open to the right.

    2. In the top-right corner, select ... and from the dropdown menu, choose View Dashboard.

      A new tab with the subscription management dashboard opens.

      Open the Dashboard

      In the left-hand navigation bar of the SAP BTP cockpit:

      Only users who are assigned to one of the roles specified in the previous section can see the link to the dashboard and open it.