Custom multitenant applications on SAP BTP and Cloud Foundry

We encounter different challenges in the IT infrastructure of our customers. For example, the following scenario: There is a central IT department and individual business groups, each with their own IT and IT infrastructure. How should licenses be purchased and provided centrally? Collecting all the requirements of the departments and business groups is confusing and cumbersome. Our team helps with a multitenant pattern for Cloud Foundry applications running on SAP Business Technology Platform. Jan Mühlbauer, Head of Development at sovanta, talked about the development of such a blueprint solution together with our customer Freudenberg live on stage at the DSAG Technologietage 2024. In this blogpost he takes us step by step through the solution.

Caution: Ideally you do have some decent knowledge with Cloud Foundry application development!

What to expect?

This blogpost will explain the multitenant pattern for Cloud Foundry applications running on SAP Business Technology Platform. The pattern will help you building your own SaaS application which will then be showing up in the service market place of your BTP global account. It will teach you the steps that are required in order to be able to subscribe and unsubscribe to your SaaS application. Further, we will explain how you can access and utilize destinations that are located in your subscribers’ subaccounts. This becomes handy if you want to perform requests towards a tenant’s ERP system (e.g. S/4HANA). Each subscriber takes care of its own destinations but the challenge is that the SaaS application and the destinations are located in different subaccounts.

The basis: terms and responsibilities

The foundation of a BTP SaaS architecture is a standard Cloud Foundry application. However, we do have to make some crucial adjustments to the specific parts in order to utilize available multitenant capabilities. We are going to explain all the required steps and provide all the information that we gained over the time.

A simple Cloud Foundry application consists of the following components:

Multitenant application on SAP BTP - the basis
  • App router module
    Each BTP Cloud Foundry application consists of an app router which serves as central point of contact for all incoming requests. It takes care that a requesting client is properly authenticated before it forwards the request to the backend or frontend module. If the client is not authenticated, it will delegate the authentication to the related identity provider.
  • Backend module
    The backend module contains all the required APIs and business logic. Client requests usually get forwarded to the backend via the app router which will enrich those requests by the user’s JWT token. With this token at hand, the backend logic can take care of user authorization.
  • Frontend module
    The frontend requests data from the backend via the app router. Communication between frontend and app router includes a JSESSIONID which gets resolved to a proper, user specific JWT token in the app router module. The app router forwards the JWT token to the backend module.
  • XSUAA service instance
    The XSUAA service instance issues JWT tokens for our authenticated users.

Requirements for a SaaS architecture for multitenant applications on SAP BTP

A SaaS architecture truly builds up on top of this simple Cloud Foundry architecture. There are some small changes required. In general, we are (semantically) differentiating between two types of subaccounts. The provider subaccount is primarily used and maintained by the developers. This is the subaccount where the SaaS application gets deployed to. Each subscriber will require its own, distinct subscriber subaccount from where the subscription gets established via the BTP service marketplace. Be aware that the subscriber subaccounts must be located in the same global account as the provider. Subscriptions accross global account boundaries are not possible. You also have to choose the same provider and region. The owner or maintainer of the subscriber subaccount depends on your organizational structure.

SaaS architecture for multitenant applications on SAP BTP

3 tasks of the subscriber subaccount owner

Before we take a closer look at the SaaS application for multitenant applications on SAP BTP itself, we would like to mention some of the tasks and responsibilities of the owner or administrator of a subscriber subaccount.

  1. Establish the subscription
    The subscription gets established via the BTP service marketplace as you do it for other SaaS applications (e.g. SAP Build Apps). During the subscription process, the subscriber gets a unique, tenant specific URL assigned with which he will be able to access the app. The URL will contain the subdomain of the subscribers subaccount (e.g. https://[subscriber-subdomain]-appname.cfapps.xyz.hana.ondemand.com). Since all tenants will send requests to our SaaS application via there specific URL, it is crucial to have that subdomain contained (the app router actually expects it) in order to properly digest to which tenant a certain request belongs and to which subscriber subaccount authentication should get delegated.
  2. User authorization
    The subaccount admin can authorize its application users by assigning users to role collections, just as he would do it for ordinary Cloud Foundry applications. However, the role collections itself are defined within the SaaS application and they are made available as well as usable during subscription.
  3. Trust Configuration
    If desired, the subscriber can connect its own identity provider to have a seemless login experience for its application users.

SaaS Provisioning Service Instance

The SaaS Provisioning Service (technical name saas-registry) registers the application in the service marketplace inside your global account. A subscription from subaccounts is only possible within the same global account and region. The service instance does not require specific bindings to one of the modules. The relationship to our application must be established via the configuration which you provide during the instantiation process.

Example:

Multitenant applications on SAP BTP: SaaS Provisioning Service Instance

It is crucial that the xsappname matches with the one of your XSUAA service instance. The displayName, description and category attributes define how your application tile in the marketplace is showing up. Use the category attribute to group the tiles of multiple SaaS applications together. The saas-registry expects that the backend implements various callback handlers which we will explain later along with some examples. However, you have to provide the URLs to those callback handlers in the configuration.

Multitenant applications on SAP BTP

After instantiating the saas-registry service with the configuration we just explained, the appropriate application tile will already show up in the marketplace. However, you will not be able to subscribe to the app, yet. Therefore some more changes need to be applied:

XSUAA

The XSUAA service instance needs to be set into multitenant mode. To do that, set the tenant-mode value to shared. This will share the same client secret across all subscribers. Further, make sure that the xsappname matches with the one of your saas-registry service instance. To increase security, it is recommended to define a scope to assign to requests that are coming from the direction of the SaaS provisioning. We will use this scope in our callback handlers to check authorization. Other than that, you can define additional scopes, roles and role collections as you would expect from regular Cloud Foundry applications.

Multitenant applications on SAP BTP: XSUAA

App router

In a multitenant scenario, the app router has some additional tasks. Since tenant requests are hitting the app router via tenant specific URLs, the app router needs to be able to understand, to which tenant a certain request belongs. Therefore, the app router extracts the subdomain of the subscriber’s subaccount from the request URL. Afterwards, it forwards the authentication to the related subaccount. You have to tell the app router how to extract the subdomain from the request URL. This can be done by setting the TENANT_HOST_PATTERN environment variable in the app router module:

Multitenant applications on SAP BTP: Tenant Host Pattern

If authentication has been successful, XSUAA will issue a JWT token for the requesting client which will contain information about the user, its assigned scopes as well as information about the appropriate subscriber. This information can then be utilized in the backend module.

Backend module

You will implement the APIs and application logic like you would do in a non-multitenant scenario. However, you should utilize the subscriber information that is transparent in the requesting clients JWT token. Additionally, you will have to implement certain callback handlers that are required by the saas-registry service and which will be called at certain moments.

Finally: subscribe & unsubscribe

The subscribe callback handler will be called during the subscription process. The main idea is to provide a place where you can provision your new tenant, e.g. by creating a dedicated database or schema. As per convention, the handler is supposed to determine and return the subscriber specific URL to the application (the app router). This URL must contain the subscriber’s subdomain and also has to comply to the TENANT_HOST_PATTERN defined in the app router’s environment.

You should also ensure that the requesting client is authorized by checking if the JWT token contains our previously defined scope.

Multitenant applications on SAP BTP: Subscribe callback handler

The unsubscribe callback handler is called during the unsubscribe process. You can cleanup, delete or archive the tenant specific database or schema and do additional things to deprovision your tenant.

Multitenant applications on SAP BTP: Unsubscribe callback handler

Now it is possible to subscribe and unsubscribe to the SaaS application showing up in the marketplace. Lets take a look at an advanced topic regarding multitenant applications on SAP BTP.

Access destinations of subscriber

Since we want to perform requests from within our SaaS application to our subscribers’ S/4HANA tenant, such destinations have to be setup in the subscriber’s subaccount. In our example, our SaaS application expects a destination with the hardcoded name “s4-hana” (of course you could make the name somehow configurable via a configuration or an UI). The type of destination depends on your particular case. For us it is important that the application user’s identity gets propagated into the S/4 system. That’s why we’ve chosen an OAuth2SAMLBearerAssertion destination which is able of performing the necessary OAuth flow on behalf of the user. Have in mind that this requires a trust relationship between subaccount and S/4 system.

SaaS architecture for multitenant applications on SAP BTP with Destiantions

Dependencies callback handler

The implementation of the dependencies callback handler is optional. If provided it will get invoked right before the subscribe handler. This handler is supposed to return a list of dependent services. Only reusable services are considered here. A reusable service is a service which implements the SAP service broker API and utilizes the SaaS provisioning service. According to our knowledge, there is no consolidated list of such services. However, we do know that the destination service is a reusable service.

By declaring the destination service instance as a dependency, we are allowed to authenticate towards that service on behalf of the user. And that will allow us to later retrieve and use destinations that are defined in the subscriber’s subaccount.

Multitenant applications on SAP BTP: Dependencies callback handler

The declaration of the destination service as dependency is fairly easy. Simply read the binding information of the destination service from the environment and return its xsappname, wrap it inside a list and return that list. As a consequence, the new subscriber will not only subscribe to our application but also to all of its dependencies and their dependencies by recursively iterating through the dependency tree. You will find an overview of all current subscribers and the whole dependency tree by accessing the Subscription Management Dashbord. You can access it by clicking on the saas-registry service instance inside the provider subaccount.

Subscription Management Dashboard showing a single subscriber and the dependency tree. Note that the desination service itself has additional dependencies.
Subscription Management Dashboard showing a single subscriber and the dependency tree. Note that the desination service itself has additional dependencies.

Example API which utilizes a destination located inside a subscriber subaccount

Since we did setup our SaaS architecture, implement all the required callback handlers and declare the destination service as dependency, it is finally time to take a look at a simple example which retrieves and utilizes a destination which is located inside the subaccount of the requesting subscriber.

Multitenant applications on SAP BTP: Destination located inside the subaccount of the requesting subscriber.

With the requesting clients JWT token, we do know who the user is, for which scopes he is authorized and to which subscriber he belongs. This token is actually required to fulfill the next step: retrieving and resolving the destination. Therefore we are using the getDestinationFromDestinationService function which is provided by the connectivity framework of SAP’s Cloud SDK. This function allows us to search in the subscriber subaccount for a destination with a specific name (the name our application expects). Because we do provide the JWT token of the requesting user, the function can authenticate towards the destination service on behalf of the user and further retrieve the destination from the user’s subscriber subaccount.

Additionally, this function authenticates the user towards the S/4 system which is abstracted by the destination by performing the authentication flow (also known as principle propagation). The function returns the destination information along with a valid bearer token, issued for the requesting user. This information can then be used to assemble the request towards the business partner API of the subscribers S/4 tenant. You can activate a token cache by setting the “useCache” flag to true. This will have a significant impact on the runtime if you have to perform multiple requests towards the same system for the same user since it will store and maintain the token for the user and skip recurring user authentication.

Any questions regarding multitenant applications on SAP BTP?

We hope that this guide will help you to set up your own SaaS architecture for multitenant applications on SAP BTP. Please feel free to share feedback and ask questions. This guide only covers the basic things required to build a SaaS application. There are some more advanced topics which we could cover in additional guides, based on the feedback we receive. Two examples may be:

  • Utilizing custom wildcard domain to facilitate onboarding of new tenants
  • Comparing this pattern with the MTX plugin of SAP’s CAP framework. You will realize that the pattern is utilized by this framework and will be able to comprehend how it works and derive advantages and disadvantages.
Jan Mühlbauer: Software Development
Jan Mühlbauer
Chief Technology Officer

Your Contact

Jan Mühlbauer has been taking care of the implementation of our solutions as Head of Development since 2018 and was appointed to the management board of sovanta AG in 2021.
Tags
Extensions SAP Business Technology Platform Software Development