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: 
quovadis
Product and Topic Expert
Product and Topic Expert














SAP Business Technology Platform (BTP) offers a hundred of business services and a number of runtime environments, namely:

that customers and partners developing cloud native applications can choose from.

Furthermore, SAP BTP offers an intrinsic, both very comprehensive and affordable, multi-tenancy mechanism with its SaaS Provisioning Service

And the good news is this mechanism can be applied to any of these three runtime environments. In this brief, we shall see how to make the best of it with SAP BTP, Kyma runtime environment.

Putting it all together


Here goes the agenda for this brief. Part A is dedicated to BTP multi-tenancy model applied to Kyma. Part B is dedicated to single sign on.











A. Developing Multi-tenant SaaS applications with SAP BTP, Kyma runtime, SaaS Provisioning and Destination services B. Easy routing with Kyma API rules and OAuth2SAMLBearer destinations





Multi-tenant SaaS applications with SAP BTP, Kyma runtime


SAP BTP runtimes are great for developing extensions.

But when it comes to Kyma, we are putting the bar much higher and targeting Kyma as a comprehensive runtime environment to develop fully-fledged multi-tenant SaaS applications.

Multi-tenant architectures for SaaS applications.


If you are planning to offer the same service to multiple consumers featuring zero administration, principal user propagation and business data separation ...then multi-tenancy is an obvious architectural choice.

SAP SaaS provisioning service with Kyma runtime environment. (saas-registry)


Nowadays, the kubernetes-managed, containerised workloads are becoming increasingly popular, almost ubiquitous.

These workloads can either act as standalone micro-services or otherwise be orchestrated as fully-fledged Software as a Service (SaaS) applications.

Thus, ultimately, we shall need a mechanism to interact with a Kyma runtime-hosted SaaS application, through a set of well-defined and secure connection endpoints, featuring both accessibility enforcement and observability.

  • SAP BTP, Kyma runtime's API rule makes it fairly easy to securely expose micro-services and their endpoints to the external world. Please refer to the appendix for further details.

  • SAP SaaS Provisioning Service APIs make it fairly easy to expose a multi-tenant solution with workloads running on Kyma.



SAP BTP multi-tenancy with Kyma environment.


SAP SaaS Provisioning Service offers an easy way to promote or demote consumers access to SaaS applications services.

  • The multi-tenant approuter and frontend application are deployed as highly available workloads in the Kyma cluster labeled Multitenancy.

  • For higher resilience, SaaS application routes are implemented with both sapse/approuter intrinsic routing and in a separate [serverless] workload with the @sap/cloud-sdk destinations.

  • The SaaS application itself is hosted in a SaaS LOB Kyma runtime (either managed or open source) possibly without any SAP BTP tie-in.

  • How and what access consumers get is regulated through a default set of configurations (=destination definitions) available at a provider. These initial configurations can be superseded and/or customised by providing the same-named destination definitions separately at each consumer tenant...









SAP BTP multi-tenancy with Kyma environment



Hybrid designs with service instance sharing. Don't fix what ain't broken.


Q. What if you had an existing multi-tenant application with SAP BTP Cloud Foundry or ABAP environments ? It is already multi-tenant solution so why would you have to migrate it to for instance Kyma ? At the same time the next generation of product functionalities could greatly benefit from Kyma qualities?


A. The good news is the above architecture caters for a such a use case as well. Nothing really prevents you from expanding your CF/ABAP-based solution into Kyma/Kubernetes.


Nowadays, [for all BTP services that are cross-consumable and support instance sharing], SAP BTP service broker allows for service instances to be shared from one runtime environment to another…


Thus, all the roles, roles collections, other security settings, destination definitions become shareable between CF/Other and Kyma/Kubernetes environments.






Last but not least, there ain't no good SaaS solution without a solid principal user propagation mechanism. Please continue reading...



Easy routing with OAuth2SAMLBearerAssertion destinations.


User Propagation (SSO) from SAP BTP Provider and Consumers sub-accounts
into Kyma with BTP destinations.


The below scenario is based on the following concept Option 2 - Setting up Trust between Subaccounts in Different Regions | SAP Help

The identity of a business user logged into a Consumer A tenant is validated by an identity provider (IDP) of the respective consumer subaccount 1/region 1.

Subsequently,  the same user may want to reach out to services available at a remote resource - SaaS LOB application - hosted in a Kyma cluster without the need to re-authentify…but with the relevant permissions.

The consumer user JWT token will be passed in the x-user-token header when calling the destination's find api















SAP BTP subaccount 2/region 2 has a custom IDP for user authentication and has an xsuaa-based OAuth2 client to request a bearer access token by calling the token issuance endpoint,

The token issuance endpoint is the AssertionConsumerService URL of the subaccount 2 (recognisable by its suffix alias/quovadis.aws-live-eu10)

As depicted above, SAP BTP subaccount 2 is in a BTP region 2 (region == data center) different from Provider/Consumers BTP region 1.

This offers an elegant way to overcome the hard data center tie-in between Provider and Consumer sub-accounts. And eventually achieves a region-free SaaS multi-tenancy.

Configure User Propagation from SAP BTP consumer tenant into a Kyma-hosted SaaS applications


Configure user propagation (single sign-on), using OAuth2 communication from the SAP BTP consumer tenant to SAP BTP, Kyma runtime.

As OAuth2 mechanism, we shall be using SAML 2.0 Assertion flow with the  OAuth 2.0 SAML Bearer Assertion destinations











weather-anywhere API destination definition (Provider/Consumer) weather-anywhere API rule definition (kyma cluster)

Please refer to User Propagation between SAP BTP and third-party Applications. | SAP Blogs and to notes in the appendix below...

MS Teams integration


It is fairly easy to embed the frontend application into MS Teams (either web or desktop Teams) for provider and consumers tenants likewise, for instance:








There is no much magic behind this integration. Pick the URLs from the Subscription Management Dashboard and add them as websites in Teams:








Just make sure to add https://*.microsoft domain name to the trust settings in every provider/consumer BTP sub-account...

Conclusion


Q. How to build comprehensive and affordable software solutions?



  • The choice of an application runtime is always a bit opinionated per se. Budgets, skillsets and politics will have their say.

  • Regardless, what really matters is the power of the underlying business platform with its services.

  • That’s the ease of consumption and the degree of integration of the platform services that will likely determine the choice of a runtime environment.

  • Less is more. Whatever qualities can be kept away from the business logic is more. And whatever quality must be baked into it is less.


Q. Why choose Kyma runtime ?













Kyma runtime offers intrinsic auto-healing, auto-scaling and high availability (multi-zone worker nodes) cloud qualities. Kyma runtime main ingredients



Top it with the built-in open telemetry support,  service mesh, serverless framework, eventing, stable egress IP addresses, enterprise grade backing services and provisioning location transparency, etc.

And that helps, as none of this needs to be baked into the business logic.

That combined with SAP BTP built-in multi-tenancy and destinations services offers a comprehensive framework geared towards building affordable SaaS solutions.

Last but not least, I hope you have enjoyed reading this blog post. Feel free to provide feedback in the comments section below.

 




Appendix


Accessibility enforcement with SAP BTP, Kyma runtime API rules


Easy routing with SAP BTP, Kyma runtime API rules


Let's consider the below API rule definition with a GET/HEAD/POST access methods to a specific system endpoint.

The most important part of the below API rule definition is the accessStrategies config as follows:
apiVersion: gateway.kyma-project.io/v1beta1
kind: APIRule
metadata:
name: "{{ .Values.services.srv.name }}-{{ .Release.Namespace }}"
spec:
gateway: {{ .Values.gateway }}
host: "{{ .Values.services.srv.name }}-{{ .Release.Namespace }}.{{ .Values.clusterDomain }}"
rules:
- accessStrategies:
- config:
jwks_urls:
- https://<tenant>.authentication.<region>.hana.ondemand.com/token_keys
trusted_issuers:
- https://<tenant>.authentication.<region>.hana.ondemand.com/oauth/token
required_scope:
- quovadis-xsuaa-master!t119894.Read
handler: jwt
methods:
- GET
- HEAD
- POST
path: /arcgis
- accessStrategies:
- config:
jwks_urls:
- >-
https://<tenant>.authentication.<region>.hana.ondemand.com/token_keys
required_scope:
- openid
trusted_issuers:
- >-
https://<tenant>.authentication.<region>.hana.ondemand.com/oauth/token
handler: jwt
methods:
- GET
path: /forecast
- accessStrategies:
- config:
jwks_urls:
- https://<tenant>.authentication.<region>.hana.ondemand.com/token_keys
trusted_issuers:
- https://<tenant>.authentication.<region>.hana.ondemand.com/oauth/token
required_scope:
- quovadis-xsuaa-master!t119894.Admin
handler: jwt
methods:
- GET
- HEAD
- POST
path: /
service:
name: {{ .Values.services.srv.name }}
port: {{ .Values.services.srv.port }}

The tenant and the region values are those of the custom IDP.

Likewise, in order to further restrict access to the API rule to specific user categories only, it is possible to include additional user authorisations (scopes) that are assigned to a user JWT token via Roles and Roles Collections assigned to business users at a BTP sub-account level.

Worth mentioning, the above API rule has three rules defined, each for a different path with a separate jwt access strategy...and with different scopes.

Assert the JWT access token


A JWT access token will be passed to the API rule in the Authorization header. Subsequently, the API rule will apply the accessor logic to assert the token and eventually grant or deny access to the resource.


























JWT claim API rule config name Description
iss trusted_issuers Token issuance endpoint. A fully qualified URL with scheme, host, and sometimes with a port number and path components but with no query or fragment components. The URL definition must match one of the trusted_issuers provided in the API rule.

  • asserts that the JWT was issued by a trust worthy identity service.


scope required_scope (optional) list of scopes.

  • asserts that the requested scopes match the scopes from the JWT token


aud target_audience (optional)  list of audiences

  • asserts that the requested audiences match the aud claim from the JWT token




Signature Validation


Validates that the JWT signed with the public key of the trust-worthy identity service.

The digital signature is verified by trying an appropriate public key from the server JWK set. The used key is typically identified by the kid and the alg header parameter.





















JWT header parameter API rule config name Description
kid The "kid" (key ID) parameter is a case-sensitive id used to match a specific public key.
alg allowed_algorithms (optional) The "alg" (algorithm) parameter identifies the algorithm intended for use with the key. Defaults to "RS256".


Configure User Propagation from SAP BTP consumer tenant into a Kyma-hosted SaaS applications.


As aforementioned, SAP BTP sub-account 2/region 2 has a custom IDP for user authentication and an xsuaa-based OAuth2 client used to request a bearer access token by calling AssertionConsumerService URL (ACS) as a token issuance endpoint,

In a nutshell, the destination service is creating a signed SAMLAssertion and then passes it to the AssertionConsumerService URL of the BTP subaccount 2 performing an IDP-initiated login on behalf of the consumer subaccount 1 user(s).

Please note, in order to be able to retrieve the delegated user permissions (scopes) from the bearer access token we need to set the OAuth2 client security descriptor authorities property.

Here goes a working sample of the XSUAA OAuth2 client's xs-security.json descriptor:
{
"xsappname": "quovadis-xsuaa-master",
"tenant-mode": "dedicated",
"authorities": ["$ACCEPT_GRANTED_AUTHORITIES"],
"scopes": [
{
"name": "$XSAPPNAME.Read",
"description": "Read Permissions."
},
{
"name": "$XSAPPNAME.Admin",
"description": "Admin permissions."
}
],
"role-templates": [
{
"name": "Viewer",
"description": "View Data",
"scope-references": [
"$XSAPPNAME.Read",
"uaa.user"
]
},
{
"name": "Administrator",
"description": "View Sensitive Data",
"scope-references": [
"$XSAPPNAME.Read",
"$XSAPPNAME.Admin"
]
}
],
"role-collections": [
{
"name": "Viewer",
"description": "Viewer (read)",
"role-template-references": [
"$XSAPPNAME.Viewer"
]
},
{
"name": "Administrator",
"description": "Administrator (read all)",
"role-template-references": [
"$XSAPPNAME.Administrator"
]
}
]
}

Good to know:

  • quovadis-xsuaa-master is the sending application and the property authorities: ["$ACCEPT_GRANTED_AUTHORITIES"] qualifies all the scopes as grantable to a receiving application


Custom Identity Provider


It is defined on a BTP sub-account 2 level:







This IDP is used only for the IDP-initiated flow with the OAauth2SAMLBearerAssertion destination(s) (it is not meant for any interactive user login).

Its x509 certificate is used to validate the consumer user SAMLAssertion when calling the token issuance endpoint of the BTP subaccount 2.
<?xml version="1.0" encoding="utf-8"?><md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="https://auth.pingone.eu/<tenant>" ID="<ID>">
<md:IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<md:KeyDescriptor use="signing">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate>
MIIFGzCCAwMCBGBb1dwwDQYJKoZIhvcNAQELBQAwUjELMAkGA1UEBhMCVVMxDDAK

XT6c0QNVYg37DBU/qhSN
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</md:KeyDescriptor>
<md:SingleLogoutService Location="https://auth.pingone.eu/<tenant>/saml20/idp/slo" Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"/>
<md:SingleLogoutService Location="https://auth.pingone.eu/<tenant>/saml20/idp/slo" Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"/>
<md:SingleSignOnService Location="https://auth.pingone.eu/<tenant>/saml20/idp/sso" Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"/>
<md:SingleSignOnService Location="https://auth.pingone.eu/<tenant>/saml20/idp/sso" Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"/>
<saml:Attribute xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" Name="user.preferredLanguage"/>
<saml:Attribute xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" Name="user.address.region"/>
<saml:Attribute xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" Name="user.id"/>
<saml:Attribute xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" Name="user.email"/>
</md:IDPSSODescriptor>
</md:EntityDescriptor>

 

Easy routing with OAuth2SAMLBearerAssertion destinations


The consumer user (BTP sub-account 1) JWT token will be passed in the x-user-token header when calling the destination's find api.

The destination service will internally create a signed SAMLAssertion and then will pass it to the AssertionConsumerService URL of the BTP subaccount 2 executing an IDP-initiated login on behalf of the consumer subaccount 1 user.



  {
"Name": "weather-anywhere",
"Type": "HTTP",
"URL": "https://weather-anywhere.<custom domain name>.com",
"Authentication": "OAuth2SAMLBearerAssertion",
"ProxyType": "Internet",
"KeyStorePassword": "<KeyStorePassword>",
"tokenServiceURLType": "Dedicated",
"audience": "https://<tenant>.authentication.<region>.hana.ondemand.com",
"Description": "weather-anywhere",
"authnContextClassRef": "urn:oasis:names:tc:SAML:2.0:ac:classes:PreviousSession",
"assertionIssuer": "https://auth.pingone.eu/<pingone tenant>",
"tokenServiceUser": "sb-quovadis-xsuaa-master!t119894",
"tokenServiceURL": "https://<tenant>.authentication.<region>.hana.ondemand.com/oauth/token/alias/quovadis.aws-live-eu10",
"tokenServicePassword": "<tokenServicePassword>",
"HTML5.DynamicDestination": "true",
"clientKey": "sb-quovadis-xsuaa-master!t119894",
"KeyStoreLocation": "quovadis_ateam-isveng.p12",
"nameIdFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
"scope": "openid quovadis-xsuaa-master!t119894.Read quovadis-xsuaa-master!t119894.Admin",
"userIdSource": "email"
}

In the aftermath, the destination will return a bearer access token in its payload.

Let's show it once again using the decoded input and output user JWT tokens


As aforementioned, the above destination gets in the x-user-token header a consumerA's JWT token from BTP sub-account 1 / region 1, for instance:
{
"alg": "RS256",
"jku": "https://<consumer tenant>.authentication.<consumer region>.hana.ondemand.com/token_keys",
"kid": "default-jwt-key--1249532834",
"typ": "JWT",
"jid": "<jid>"
}.{
"sub": "<sub>",
"user_name": "foo.bar@acme.com",
"iss": "https://<consumer tenant>.authentication.<consumer region>.hana.ondemand.com/oauth/token",
"aud": [
"uaa",
"openid"
],
"ext_attr": {
"enhancer": "XS_APPLICATIONUSER",
"zdn": "<consumer tenant>"
},
"zid": "<zid>",
"user_id": "<user_id>",
"azp": "<azp>",
"scope": [
"openid",
"uaa.user"
],
"exp": <exp>,
"iat": <iat>,
"jti": "<jti>",
"email": "foo.bar@acme.com",
"cid": "<cid>"
}.[Signature]

Next, it creates a SAMLAssertion, passes it to the token issuance endpoint, The saml assertion gets validates and eventually a bearer access token is returned with the scopes and audiences as required by a SaaS application endpoint, for instance:
{
"alg": "RS256",
"jku": "https://<tenant>.authentication.<region>.hana.ondemand.com/token_keys",
"kid": "default-jwt-key--623944614",
"typ": "JWT",
"jid": "<jid>"
}.{
"jti": "<jti>",
"ext_attr": {
"enhancer": "XSUAA",
"subaccountid": "<subaccountid>",
"zdn": "quovadis"
},
"xs.system.attributes": {
"xs.rolecollections": [
"Administrator",
"Viewer"
]
},
"given_name": "foo.bar",
"xs.user.attributes": {},
"family_name": "acme.com",
"sub": "<sub>",
"scope": [
"quovadis-xsuaa-master!t119894.Read",
"openid",
"quovadis-xsuaa-master!t119894.Admin"
],
"client_id": "sb-quovadis-xsuaa-master!t119894",
"cid": "sb-quovadis-xsuaa-master!t119894",
"azp": "sb-quovadis-xsuaa-master!t119894",
"grant_type": "urn:ietf:params:oauth:grant-type:saml2-bearer",
"user_id": "<ser_id>",
"origin": "httpsauth.pingone.eu5f3341ef-3cf9-4c",
"user_name": "foo.bar@acme.com",
"email": "foo.bar@acme.com",
"rev_sig": "b4df0449",
"iat": <iat>,
"exp": <exo>,
"iss": "https://<tenant>.authentication.<region>.hana.ondemand.com/oauth/token",
"zid": "<zid>",
"aud": [
"sb-quovadis-xsuaa-master!t119894",
"quovadis-xsuaa-master!t119894",
"openid"
]
}.[Signature]

Worth mentioning, the above exchange mechanism allows to separate the SaaS provisioning aspects with a provider and consumers from the provisioning of the SaaS application itself.

 




Additional resources






















Before you get started.





Multitenancy





Sample code repositories, gists and blogs.





Public code generators.





 




SAP Kyma Community and SAP BTP, Kyma runtime Q&A Tags

Follow me in SAP Community: piotr.tesny



 

 
6 Comments