Technical Articles
Principal Propagation to SAP CPI using OAuth2SAMLBearerAssertion
Introduction
SAP supports various Authentication methods (SAML, OAUTH, Certificate etc) to enable user authentication to its Cloud Platform Application. SAP Cloud Platform Integration an application on SAP Cloud Platform commonly uses FORM (implemented over SAML) authentication to login users to WebUI and Role / Certificate authentication to IFlow Runtime Endpoints.
The Role based authentication to runtime endpoints can be achieved using Basic Auth or OAUTH access token. While no explanation is needed for Basic Auth you can read this blog “Secure connectivity (OAuth) to SAP Cloud Platform Integration” by Divya Mary to understand using OAUTH – Client Credentials flow to send message to runtime endpoints.
In this blog I will explain how to implement OAUTH2 – SAML Bearer Assertion flow to SAP Cloud Applications and thus achieve Identity propagation from the sender.
Note: I’m using SAP CPI as a use case here but the flow implementation is same for any SAP Cloud Application resource protected using OAUTH.
OAUTH2SAMLBearer Assertion Flow
The Security Assertion Markup Language (SAML) 2.0 is an XML-based framework that allows identity and security information to be shared across security domains for SSO. RFC7522 defines how a SAML Assertion can be used to request an access token when a client wishes to utilize an existing trust relationship without a direct user approval step at the authorization server. This flow at a high level constitute below steps.
- Establishing Trust between Sender and Receiver.
- Sender Generate a Short living SAML Assertion and post to Receiver Application Token Endpoint.
- Receiver Validates the SAML Assertion using existing trust and issue an Access Token with requested Scope.
- Sender make Resource Call with the received Bearer Token.
Scenario
The principal propagation between the sender and SAP CPI are based on Open Standard Protocol and hence it’s vendor neutral. So the Identity propagation can be achieved from any APPs deployed on Private / Public Cloud or On-premise and SaaS Applications (mostly with Development effort).
In this blog I will explain how to implement Trust set-up between sender and SAP Cloud Platform, OAUTH SAML Bearer (1) and OAUTH protected resource call (2) implementation.
Note: Principal Propagation between SAP Cloud Apps are supported out-of-the-box using OAUTH2 – SAML Bearer. Hence it’s out of scope for this blog.
1. Trust Set-up
Trust set-up is achieved in two step i.e. adding an IDP and Registering an OAUTH Client in SAP Cloud Platform.
This however is a single step in most Apps like Salesforce, Successfactor etc i.e. by creating an OAUTH Client and associating the signing certificate and role assignment. Hope SAP simplify it in future.
1.1 Pre-requisite
- A Key Pair is generated (Self / CA Signed) or an Existing Keypair used by the sender application.
- Sender Application Implement logic to generate a short living SAML Assertion and sign it with Private key.
- The X.509 Certificate a.k.a. Public Key or Signing Key is shared.
1.2 SAP Cloud Platform – IDP Set-up
- From SAP Cloud Platform Cockpit Navigate to Security –> Trust –> Application Identity Provider and choose “Add Trusted Identity Provider”
- Add the sender application as a Trusted IDP like shown below.
Field Value Name Unique Name to Identity Sender Application Assertion Consumer Service Application Root (default) Single Sign-On URL A dummy URL (No significance) Single Sign-On Binding HTTP-POST (No significance) Signature Algorithm Same as the algorithm used by Sender SHA-1 / SHA256 Signing Certificate X.509 Certificate of the Key Pair User ID Source XML element from SAML assertion containing User ID. - Define the Default and Assertion Based attribute to be propagated.
- Define Default or Assertion Based Groups Assignment.
Note: I have assigned a default Group containing the required Role, however you could choose to assign Assertion- Based Groups by evaluating certain condition. Eg. Assign Group ‘X’ only if the user a member of ‘Y’ Department etc.
1.3 SAP Cloud Platform – OAuth Client
- From SAP Cloud Platform Cockpit Navigate to Security –> Trust –> OAUTH –> Clients and choose “Register New Client“
- Register a New Client to represent Sender Application as show below.
In Subscription drop-down choose your Application. In this case I have choose SAP CPI runtime.
2 SAML Assertion
The Sender Application should implement code to generate an SAML Response containing logged on user principal and sign the assertion using the Private Key.
2.1 SAML 2.0 bearer assertion Generation
The sender application generate a SAML Bearer Assertion in the below format.
SAML Assertion Element | Value |
Issuer |
Application IDP Name |
Signature | Computed Signature |
NameID | Logged on User ID |
Recipient |
SAP Cloud Platform Token Endpoint |
Audience |
SAP CP Local SP Name |
Attribute | Restrict only to require User Attributes. Eg: Email, Memeberof, First Name etc. This attribute can be used for Assertion Based Group Assignment. |
<Assertion
xmlns="urn:oasis:names:tc:SAML:2.0:assertion" ID="pfx41c65b86-321a-8659-eb60-07535d31b02b" IssueInstant="2019-05-27T07:04:51.852Z" Version="2.0">
<Issuer>{ApplicationIDPName}</Issuer>
<ds:Signature
xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<ds:Reference URI="#pfx41c65b86-321a-8659-eb60-07535d31b02b">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>IYdHJoZChCgA60prp6utKwtYCj8=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>{AssertionSignatureValue}</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>{SigningKey}</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</ds:Signature>
<Subject>
<NameID>{UserID}</NameID>
<SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<SubjectConfirmationData NotOnOrAfter="2019-05-27T09:04:51.868Z" Recipient="https://oauthasservices-{tenantID}.hana.ondemand.com/oauth2/api/v1/token"/>
</SubjectConfirmation>
</Subject>
<Conditions>
<AudienceRestriction>
<Audience>https://hana.ondemand.com/{tenantID}</Audience>
</AudienceRestriction>
</Conditions>
<AuthnStatement AuthnInstant="2019-05-27T07:04:51.870Z">
<AuthnContext>
<AuthnContextClassRef>urn:none</AuthnContextClassRef>
</AuthnContext>
</AuthnStatement>
<AttributeStatement>
<Attribute Name="mail">
<AttributeValue
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">{emailAddress}
</AttributeValue>
</Attribute>
</AttributeStatement>
</Assertion>
3. OAUTH2 SAML Bearer Assertion Flow
Sender Application Post Base64 encoded SAML Bearer Assertion to SAP Cloud Platform Token Endpoint.
Method | POST |
URL |
OAuth Token Endpoint |
Authorization | Basic ClientID/ClientSecret |
Header | Content-Type:application/x-www-form-urlencoded charset:UTF-8 |
Body | client_id:{OAUTH Client ID} scope={RequiredScope} grant_type:urn:ietf:params:oauth:grant-type:saml2-bearer assertion:{Base64 encoded SAML Assertion} |
Note: RFC7522 define using base64url encoded assertion however SAP implementation uses base64. Make a note of it and implement the Client accordingly.
Below is a sample request/response using Postman.
SAP Cloud Platform Validates the Client Credentials and SAML Assertion before issuing a Bearer access token.
4. Invoke SAP CPI Endpoint
The Sender Application Invoke SAP CPI IFlow Runtime Endpoint using the retrieved Bearer Token.
Method | As Required by your IFlow |
URL | IFlow Endpoint |
Authorization | Bearer <access_token> |
Header | As Required by your IFlow |
Body | As Required by your IFlow |
Below is an Postman example.
5. Retrieve User Principal in SAP CPI IFlow
The user Principal from the Sender Application is populated in “SAP_AuthHeaderValue” Exchange Property.
Decode it to retrieve the propagated user identity which look similar to below.
{
"name": "UserID",
"attributes": {
"IDP": "CustomIDP",
"name": "NameID",
"CustEmail": "{Mapped from SAML mail attribute}",
"com.sap.security.oauth2.clientId": "f2787804-416c-36fe-8614-d2756765ad71"
}
}
You can notice that
- Name attribute is populated as configured in Section 1.2 Step-2 “User ID Source”.
- IDP and CustEmail populated as configured in Section 1.2 Step-3 “Default and Assertion Based attribute”
- com.sap.security.oauth2.clientId populated with OAUTH Client created in Section 1.3
6. Propagate Identity to Target Application
The common principle to implement end-to-end SSO is to break down the flow into two i.e. Sender to SAP CPI and SAP CPI to Receiver and choose the principal propagation mechanism supported by receiver of each leg. While the former supports OAUTH – SAML Bearer Assertion as shown in this blog the latter can be achieved using OAUTH – JWT, OAUTH – SAML, SAML etc.
So to achieve the Last-mile security and Principal Propagation
- Retrieve the Unique User Identifier as required by the Target Application i.e. User ID or Email from SAP CPI Principal Propagation Exchange property.
- Implement SSO mechanism supported by Target Application using the user principal retrieved in SAP CPI.
In my scenario I used OAUTH – JWT Bearer Flow to propagate Identity to Salesforce as shown in this blog series “SAP CPI – Salesforce Rest API Integration using OAUTH JWT Bearer Flow“. This is reference solution and can be implemented for any Target Apps supporting OAUTH – JWT Bearer flow.
Additionally read “SAP Cloud Platform Integration – Principal Propagation with SuccessFactors OData V2” blog to know how to implement Identity Propagation to Success factors using OAUTH – SAML Bearer Assertion.
Unfortunately at this point of time SAP CPI does not support OAUTH – JWT Bearer out the box and supports OAUTH – SAML Bearer only in Successfactors ODATA Adapter. So we have to workaround this limitation for Last-mile Principal Propagation.
Hi Santhosh - Good blog, keep up the good work.
Though the overall process seems complicated but good to know this feature is supported and hope will be simplified further.
Regards
Rajesh Pasupula
Hello Santosh,
We are trying for Oauth SAML for C4C OData services but not succeed. In order to get the token need to pass the assertion value but we are unable to generate it. could you please provide the details of some elements/values you used in the saml assertion xml. I generated a self signed certificate and I have the certificate details. it would be appriciated if you can provide below details :-
1) {ApplicationIDPName}<
2) {AssertionSignatureValue}
3) {SigningKey}
4) <ds:DigestValue>IYdHJoZChCgA60prp6utKwtYCj8=<ds:DigestValue>IYdHJoZChCgA60prp6utKwtYCj8=</ds:DigestValue>
5){emailAddress}
Sheshukumar Guntuka
Hi Santhosh,
thank you very much for providing this really good guide. I was able to replicate and it works!
Now I would like to read or as you say "decode" the "SAP_AuthHeaderValue”. When looking into the log of CPI I only see "com.sap.it.rt.scc.connectivity.service.principal.propagator.impl.NeoPrincipalToken@6779d410" instead of the token you show in the screenshot.
What would be the tool I could use to decode this token? Can the attributes read in a script somehow?
Thanks!
Jonas
Hi Jonas,
I have actually had this working last week (as in getting the proper SAP_AuthHeader) but now I end up with the same error as you. Have you managed to resolve this?
Btw. you can read the SAP_AuthHeaderValue like any other property:
Groovy example:
def propertyMap = message.getProperties()
String authUser = propertyMap.get("SAP_AuthHeaderValue");
There are some examples on the net on how to decode it using groovy as well.
Cheers
Paul
Hi Paul,
thank you very much for your answer. Unfortunately, I was not able to resolve this till now... With the snipped from your response I was able to get the property and store it in another one, but it still shows as "com.sap.it.rt.scc.connectivity.service.principal.propagator.impl.NeoPrincipalToken@6779d410". Even when I try a string manipulation on it, it seems it is exactly that string. For example I used substring on the value:
def propertyMap = message.getProperties()
String authUser = propertyMap.get("SAP_AuthHeaderValue");
message.setProperty("principleparts", authUser.substring(0,10));
the result was "com.sap.it"...
I was not able to find any examples on the web how to decode it... Did you find any?
Thanks &
Best regards,
Jonas
Hi Jonas,
SAP_AuthHeaderValue will no longer work, there has been some changes in the SCI backend. However,, there is another property, SAP-Connectivity-Authentication that stores the same information so give it a try. (on the oData adapter in my case)
In addition to the above, SAP support pointed me in direction of ‘SapAuthenticatedUserName’ (you will need to add the 'SapAuthenticatedUserName' in the AllowedHeaders field of the Runtime Configuration" before you can access it)
I just need to know if this is something that will actually be documented and supported long term as I don’t want to build a solution based on feature that may change without any notification.
More test to come
Cheers
Paul
Hi Paul,
Thanks for the reply.
Just for the sake of completion. There is a way to retrieve the value of the property, even if it is an object reference.
The script that works for me:
Outcome is basically a map that has the class and the value of the property in a map.
Best regards,
Jonas
Hi Paul, Jonas,
Thank you for discussing here to give a opportunity to improvise.
It was the “SAP-Connectivity-Authentication” header that had the value always (which is shown in Exchange Property SAP_AuthHeaderName), and the corresponding header value was stored in a Exchange Property called “SAP_AuthHeaderValue“. I haven’t tested now but it seems like this property variable can also contain an object instead of value itself.
Then it’s robust to read the Authentication value from Header instead of Exchange Property. I will update the blog with this and also code snippet to decode it.
It was also good to know about “SapAuthenticatedUserName” Header which contain the authenticated user name populated from NameID.
Thanks,
Santhosh.
Hi Santhosh
Good blog to start on Principal Propagation in CPI.
Is it possible to pass this Authentication header value received from the sender system to the SAP On-premise system throguh RFC or SOAP via Cloud Connector.
Thanks
Hi Parthiban,
I've read that SAP Cloud Connector has Principal propagation set-up which should be like a out-of-box solution to achieve this. But I've haven't personally worked on it yet so it's a unknown to me.
What I know certainly works is SAML based Principal propagation to Backend. But this is not supported OOTB in SCPI and require a workaround.
Thanks,
Santhosh.
Hi Santosh,
Thanks for the detailed blog.
But as per my understanding the whole idea of this blog was to capture the user id in the cpi iflow , if that is then why setup Oauth ? we can simplify use basic auth and add SAP authenticated user in allowed headers . In this way I can still capture User id into the iflow. correct me if I am wrong.
Regard
Kiran
Hi Kiran,
The idea of the blog is to achieve user propagation from Sender to SAP CPI to Receiver i.e. to support SSO. User propagation means not propagating a static/single user configured as Basic authentication, but any dialog user carrying out the interface operation from source to target.
Thanks,
Santhosh
H Santhosh
We are trying out to propagate the user present in the payload of the inbound message received in CPI to Success factors using OAuth SAML assertion. Is there way to pass the user name dynamically. We have set up the Oauth client configuration in Success factors. Request your advice on this.
Hi Santhosh,
Can you please let me know what could be the value to be passed to "AssertionSignatureValue".
I could not find any information regarding this parameter anywhere.
Also do we need to replace DigestValue value under SignedInfo node? if, yes, what couold be that value.
Regards,
KusumaK.
Hi Santhosh Kumar Vellingiri,
We are trying to achieve SAP Priniciapal propagation in similar way, but SAP told me that, Oauth2/token supports only 3 grant types (client_credentials, authorization_code, implicit). Was this available before ?
thanks,
Naveen
Hi Naveen Chamala,
that's what I got from SAP, too.
In addition to your comment, I'm wondering how we should establish Principal Propagation to SAP On-Premise environments in cases where we don't want to use a technical user for the Back-End while switching the authentication method?
Instead, the iFlow gets called by a third-party application on behalf of an individual user.
Best regards,
Sebastian
Hi Sebastian, We managed to resolve this after a support call with SAP. You can achieve PrincProp. The key point is, SAML assertion IDP should be apimgmt. Follow the steps (IDP metadata) from this blog
after getting SAML (base64), pass that to oauth service (use relavent oauth client) to exchange Bearer token.
Hope this will help.