Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
former_member322100
Active Participant
In the context of Cloud Computing, Multitenancy allows an application provider to serve applications and services to multiple tenants – a set of users. End users of the application access it through a URL dedicated to that tenant.

A tenant-aware application has the capability to separate data securely per tenant, can share resources across the tenants, and can serve features from a single code repository.

This blog elaborates on the process to build a multitenant application on SAP Cloud Platform in the Cloud Foundry environment. For multitenancy on the Neo environment, please see this blog.


Understanding Software as a Service (SaaS) Provisioning service


In the SAP Cloud Platform Cloud Foundry environment service marketplace, we can see SaaS Provisioning service as shown in the image below.



To make our SaaS Application available to multiple tenants we must set up a registry for our application. The SaaS Provisioning service is where we, as a SaaS Application provider, register our app. The SaaS Provisioning service enables us to automate the subscription process. It also maintains a list of all dependencies and subscriptions of an application.



A SaaS Application provider creates a subaccount to deploy the application. Inside this subaccount, the provider creates a space and deploys the SaaS Application. The provider then registers the application with a SaaS registry. The SaaS registry service on SAP Cloud Platform creates a new entry. Through this service, any sub-account, within the same global account, will see an entry of the provider application in the subscriptions tiles. The consumer then can subscribe to the application by just a click of a button. Each subaccount has a unique subdomain and identity zone id.

 

Understanding the concept through an example


Let’s look at how one can use SaaS Provisioning service to build a simple ‘Hello World’ multitenant application.

There is a SaaS Application provider - Provider ZZZ. Provider ZZZ would like to build an application that displays a hello message. The hello message will display the logged in user's name and the tenant from which the user has logged in - as shown in the image



 

As ZZZ, we will build a ‘Hello World’ application to display the 'hello message'. Also, since each tenant will access the application from a unique URL, we will build an app router app to resolve the URL, authorize the request and forward the request to our application.




Overview of the Steps Involved



1.     Develop A SaaS Application


In our application’s default end-point (GET – ‘/’) we will return a hello statement displaying the username, tenant subdomain, and tenant identity zone.

We deploy this application on our (provider’s) sub-account and test the application. Once we are satisfied with the development, we add the configurations required to make our application available across tenants.

 

2.     Add Configurations For Multitenancy


To make our application available to SaaS Provisioning service and to automate the subscription process we must add the following configuration to our SaaS Application.

The SaaS Provisioning service requires us to define the following API end-points.

  • PUT - ‘callback/v1.0/tenants/*

    • To onboard a tenant, i.e. when a consumer subscribes to the application





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

    • To off-board a tenant, i.e. when a consumer unsubscribes to the application




Each consumer accesses the SaaS Application through a unique URL. To ensure that these requests come to the target application, we define a tenant host pattern for our app router app in the MTA.yaml file. Click here for the detailed documentation.

We specify the SaaS Application name, description, app category while creating a SaaS Provisioning service instance. We mention these details in a config.json
{

"appId": "<XS-App Name>",

"displayName": "Hello World",

"description": "A sample hello world app to explain the concepts of Multitenancy",

"category": "Provider ZZZ",

"appUrls": {

"onSubscription": "<Your back-end app URL>/callback/v1.0/tenants/{tenantId}"

}

}

 

3.     Register the SaaS Application to SaaS Provisioning


Upon completing our configuration, we create a SaaS Provisioning service and bind it to our SaaS Application. To complete this set of actions we require Cloud Foundry Command Line Interface.

We start this process by creating a new SaaS Provisioning service instance.
cf cs saas-registry application <Service name> -c <Path to config.js file>

Next, we bind our SaaS Application to the newly created SaaS Provisioning service instance.
cf bs <SaaS Provider Application Name> <SaaS Provisioning Service Name>

Once we bind our application to this service instance we will be prompted to restage our application. To do so, we execute the following command.
cf restage mt-hw-node-app


 

4.     Subscription of the SaaS Application by a Consumer


A consumer can subscribe to the application either through the SAP Cloud Platform Cockpit or through REST APIs.

Subscription through SAP Cloud Platform Cockpit


In your global account, create a new subaccount and go to the Subscriptions tab. The SaaS registry service shows the applications that have been registered with it. Here, you will find the provider’s application.



Click on the application tile and click on the subscribe button.


Subscription through REST APIs


You will require a client that can make REST API calls (such as Postman).

We first get the application authorization token. Using your Cloud Foundry Command Line Interface tool execute the following command
cf env <Name Of You SaaS App>

Create a new request in your client with the following setup. Copy the corresponding values from the environment variables.
HTTP Method – POST

URL – <"System-Provided"."VCAP_Services"."saas-registry"."credentials"."url">

Parameters

Key: grant_type

Value: client_credentials

Authentication Type – Basic Authentication

Username - <"System-Provided"."VCAP_Services"."saas-registry"."credentials"."clientId">

Password - <"System-Provided"."VCAP_Services"."saas-registry"."credentials"."clientSecret">

You will receive the following as a response.
{

"access_token": "xxxxxx",

"token_type": "bearer",

"expires_in": 43199,

"scope": "yyyyyy",

"jti": "zzzzz"

}


We use the access_token returned to call the subscription API in the next step.
HTTP Method – PUT

URL - <"System-Provided"."VCAP_Services"."saas-registry"."credentials"."tenant_onboarding_url">/api/v2.0/subscription/tenants/{tenantId}

tenantId – Tenant Zone Identity

Parameters

Key: jobUuid

Value: A newly generated GUID

Authentication Type – OAuth 2.0

Access Token – Access token copied from the response of the earlier API

In upcoming blogs, we will take a look at the different APIs that can be used.

 

5.     Mapping Consumers’ Routes


In the last step, the provider needs to add a new route to ensure that a consumer’s request goes to the app router.





You can now launch the app for the consumer

In upcoming blogs, we will take a closer look at methods that we can adopt to automate the process of route mapping.

 

 

Please find the code for a ‘Hello World’ sample application here. This application has been developed using node.js. For further information and a step by step guide, please go through the Readme.MD file. If you prefer a video, please check out this playlist by SAP HANA Academy.

 

Now that you have an understanding of SaaS Provisioning and steps involved in building a multitenant application you can go through this blog to see how can we achieve multitenancy at persistence layer through SAP Cloud Platform, Cloud Foundry environment. The blog explains the concepts through a sample product inventory management application. To gain a deeper understanding of the architecture please read this blog.
82 Comments
SamueleBarzaghi
Participant
0 Kudos
Hi Jan,

We are interested too, can you explain how to achieve this?

Thank you

Best Regards,

Samuele Barzaghi
former_member189718
Active Participant
0 Kudos
Hi Samuele,

this can be achieved with the getDependencies callback as mentioned here: https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/3971151ba22e4faa9b245943fee...

I guess, you have a multitenant application? When this gets subscribed, you have to return the dependency to the destination service and thus this one gets subscribed as well. Afterwards you should be able to read the destinations from the consumer tenant.

Some details could be found here: https://help.sap.com/viewer/cca91383641e40ffbe03bdc78f00f681/Cloud/en-US/7e306250e08340f89d6c103e288...

Best regards,

Jan
SamueleBarzaghi
Participant
0 Kudos
Hi Jan,

Thank you

Best Regards,

Sam
sangitapurkayastha
Discoverer
0 Kudos

Hi Jan,

We have a similar requirement. We have created a multi-tenant fiori application hosted by the provider subaccount and we are successfully able to subscribe to the same from the consumer subaccount. But now, we would want the consumer application to read the data from the destination in the consumer subaccount itself.

The first thing that you mentioned to achieve this, is to include the getDependencies while registering the multi-tenant application with the Saas Provisioning service. Once this is done what would be the next steps involved? What are the changes required in the fiori application created using web ide to enable it to read the destination from the consumer account?

Can you please point to any blog or any detailed documentation on how this can be achieved?

Regards,

Sangita Purkayastha

 

 

former_member189718
Active Participant
0 Kudos
Hi Sangita,

you also have create an instance of the destination service and bind it to your application. After doing this, you find the credentials for your application in the environment variables.

Now, when you want to access the destination service, you have to exchange the token and then forward the new token to the destination service.

Best regards,

Jan
sangitapurkayastha
Discoverer
0 Kudos
Hi Jan,

After having subscribed to the application successfully from my consumer subaccount, I would now want to read the destination from our consumer subaccount.

Since my application is hosted in the provider subaccount (and we were reading the destination until now from the provider subaccount), so binding the application with the destination service instance is only possible in the provider subaccount. How can we do this for the destination in the consumer subaccount?

Regards,

Sangita Purkayastha
former_member189718
Active Participant
Hi Sangita,

same as described before: you have to bind the destination service to your application within your provider account. After providing the destination service in the getDependencies() callback, the destination service is subscribed for new subscriptions. This "forwarded" subscription allows you to read the destinations for the consumer accounts.

Technically, you have to exchange the token with XSUAA, so that the correct information is contained in it. With this new token, you then call the destination service.

Best regards,

Jan
AntonyJerald
Advisor
Advisor
Hello sandeep.tds

Thanks for the wonderful blog.

Do you have any blog that automates the process of route mapping manually?  If so, could you please attach the link here?  Thanks in advance.

 

with Regards,

Antony Jerald.
former_member322100
Active Participant
0 Kudos
Hi antonyjerald.joseph,

Thank you so much for your kind words.

You can automate the manual steps mentioned in this blog by using Custom Domains.

 

I hope this information proves to be useful.

 

Warm Regards
Sandeep T DS
marcmaurí
Participant
0 Kudos
Hi ravindrapawar ,

I got the same issue. Did you solve it?

Thanks in advance,

Marc
former_member194549
Contributor
0 Kudos

Hi Ashish

have you solved this problem? I’m encountering the same issue.

The logs show the following error message:

GET request to /cp.portal/site completed with status 500 - Call to /oauth/token was not successful (grant_type: user_token). Bearer token invalid, requesting client does not have grant_type=user_token or no scopes were granted.

I have already assigned the scope uaa.user to the user in consumer and provider account.

Regards
Simon

iashishsingh
Participant
0 Kudos
Hi Simon,

In my case, there was an issue with the subscription which was fixed by the SAP support team. I had logged a ticket with SAP.

The error you are seeing looks different. If you are sure to have followed the role collection assignments for the subscribers as well, I would suggest you to log a ticket with SAP.

- Ashish
0 Kudos

Hi Ashish,

 

I am facing a similar issue as yours. i am getting the following error on accessing /cp.portal from subscriber account. Did you solve the issue?

@Sergio Rozenszajn Do you have any idea on this?

"GET request to /cp.portal/site completed with status 500 - Call to /oauth/token
was not successful (grant_type: user_token). Bearer token invalid, requesting client does not have grant_type=user_token or no scopes were granted."
iashishsingh
Participant
0 Kudos
Hi Tapishnu,

The error you are seeing points to a missing Authorisation step for the subscriber user. I think it should go away if proper steps are followed.

– Ashish
Hi Sandeep,

Thank you for the awesome blog.

I had successfully deployed the multi tenant application  when i open the app-router application  i am getting error “The subdomain does not map to a valid identity zone.”

Can you please provide solution for this.

Thanks in advance

 

Regards

Daraksha
former_member322100
Active Participant
0 Kudos
Hi Daraksha,

Thank you so much for raising your issue.

Your issue seems similar to an issue raised earlier. Does the answer mentioned here help?

Warm Regards
Sandeep T D S
0 Kudos
Thanks a lot sandeep.tds for write up with simple example.

I have few questions on the scope of SaaS subscription limited to a global account.

  1. How do a global account and sub account is provisioned to customers i.e. tenants? As a software provider, do you need to create sub accounts for all customers in the global account where provider sub account is present?

  2. Can you provide a business use case where SaaS provisioning would be useful? I mean who would be the provider and who would be the targeted consumers.

  3. How does the API Service be designed with respect to provisioning? Let say I have a API in one of the sub account and I want other application on different other global accounts to use it.

  4. How does it look if I want to develop a API service with multi tenancy? Let say a multi tenant application in SAP CP of other global account uses API Service and API service want to ensure application accesses API service on behalf of a tenant. i.e. via some auth mechanism.

WRoeckelein
Active Participant
0 Kudos
Hi sandeep.tds ,

thanks for this blog, which is still very helpful.

One question: Should we have one saas-registry service per multi-tenant application or can several multi-tenant applications in the same space share a common saas-registry service?

Thanks,

Wolfgang
0 Kudos
Hi Sandeep,

 

Thanks for the blog.

One question is, If in the new version of the application, if we change the go to application url in the subscription callback, then is there a way to refresh all the existing subscriptions to point to this new go to application URL ?

 

Thanks

Pradeep
swetha_balusamy
Employee
Employee
Hi  sandeep.tds,

Thanks for the blog.

We have exposed a SaaS application in CF canary landscape.After subscribing when I launch the application , it gives "Your connection is not private"error as shown below.


Do you know why such an error comes?

Regards,

Swetha.
former_member322100
Active Participant
0 Kudos
Hi Swetha,

Thank you for your question.

As far as I can tell, your application URL is not trusted.
You need to add this URL format as a trusted URL.

However, you will not face this issue in production systems.
To mitigate the issue, you would have to create custom domains.

Please see this comment for more details. 

 

Warm Regards

Sandeep T D S
former_member322100
Active Participant
0 Kudos
Hi Wolfgang,

Thank you for raising your question.

I personally recommend using one instance of saas-registry service per multi-tenant application. Doing so would help you in maintaining your application.

Warm Regards
Sandeep T D S
former_member322100
Active Participant
0 Kudos
Hi Pradeep,

 

I've written to your sap email id.

 

Warm Regards
Sandeep T D S
former_member322100
Active Participant
0 Kudos
Hi Satish,

 

I've written to your sap email id.

 

Warm Regards
Sandeep T D S
antonette_oberholster
Active Contributor
0 Kudos
Hi Sandeep

Thanks for the great article.

I'm trying to develop a shell plugin using a launchpad module, I followed all the steps in the documentation and I'm able to successfully subscribe to the subscription. Routes were also configured and I can run the app in a different subaccount using that route url. However, I need to integrate this plugin with the Portal service and I do not see the plugin listed as a content provider in the Content Explorer of Site Manager in the Portal.

Do you have any advice on how to use the newly created subscription as a content provider in Portal? I've followed the steps described here: Expose Your App as a Content Provider

Regards

Antonette
0 Kudos

Hi Sandeep.

I have a app specific (not tenant specific) url which shows the api documentation of my application. I wish to add just the basic username and password authentication on this url (so that its accessible to only internal saas owners) while all the tenant specific routes should continue to work the way as you mentioned. Do you have any suggestion on how can I add a new addition in either app router or tenant host pattern to include a app specific route with just simple username and password authentication as this url is just for internal saas app owners and I dont wish to expose it to the public on internet?

FYI, I tried mapping this app-specific route (say, SaaSAppName.Domain/v1/docs) to my app router and then, accessing the url redirected me to my app router for login. But my login fails with error: Unable to verify username or password. Please try again. 

I have already configured the spring security for this route as well in my Security class (I am not checking scopes for this url): .mvcMatchers(GET, "/v1/docs").authenticated();

 

Regards.

Kush Sharma

former_member585626
Participant
0 Kudos
Hi Sandeep,

I have built  a search engine which is an entity agnostic, rest api exposed and built using opensource apache Lucene. Wanted to make it as a SaaS service, so that other product/s can use/bind it   easily.

I just need to push as a SaaS service, is there quick way to do that.

Thanks

Mohan Ramu
gregorw
Active Contributor
0 Kudos
Hi Jan,

thank you for this links. They where the missing piece to get at least Internet destinations from the Subscriber Subaccount working. But I'm still struggling with the OnPremise Destinations. I've provided only the destination service as a dependency. But from this screenshot of the Subscription Management Dashboard you can see that the connectivity service was also added to my subscription:


But my CAP client fails with this error message:

"Error during request to remote service: Failed to add proxy authorization header - client credentials grant failed! Caused by: Could not fetch client credentials token for service of type "connectivity"

Can you confirm that at least the dependencies look right?

CU
Gregor
gregorw
Active Contributor
Hi Jan,

I was able to solve it on my own. I needed to make also connectivity a direct dependency. Now also the OnPremise Destination works.

CU
Gregor
0 Kudos
I'm trying to follow the instructions here
However, I don't see any xs-security.json
0 Kudos
I think it's not possible.. both provider's and consumer account should belongs to same infrastructure provider.
0 Kudos
It's not possible.. both provider's account and consumer account should belong to same infrastructure provider.