Technical Articles
How to troubleshoot SAP BTP OAuth2SAMLBearerAssertion destination with SuccessFactors?
|
SAP BTP is a business technology platform with so-called kernel or built-in-in services like connectivity, identity, application router etc and application runtimes like Cloud Foundry, Kyma (SAP managed kubernetes), ABAP (steampunk) or Other (any bespoke runtime)
Disclaimer:
|
How to troubleshoot SAP BTP OAuth2SAMLBearerAssertion destination with SuccessFactors or any other LOB application? Especially when the SFSF BizX/other LOB user’s identity is represented by an OIDC token? Let’s see how to have destination service help troubleshoot a saml assertion that is generated internally by an OAuth2SAMLBearerAssertion type of destination, especially when having the user principal encoded in one of the supported user claim attributes of a user’s JWT token (i.e. OIDC identity).
|
Putting it all together
By default, the user principal is identified by one of the following JWT (JSON web token) attributes: ● user_name ● email ● mail ● user_uuid ● sub. When using OAuth2SAMLBearerAssertion destination with SAP BTP destination service there is no way to look up the saml bearer assertion that is being passed into the token endpoint of the destination OAuth client. As security is paramount, this is by design, as neither the saml assertion nor the private key used to signed it are ever disclosed. But again our polyvalent destination service offers a way to generate the saml assertion – using SAMLAssertion destination type. Good to know:
Please note:
|
Let’s do it by example. Problem statement.
Recently, I got a question regarding the following error message [LGN0022]Person-only not supported when gace flag is off. The error occurs when trying to call into an inbound SFSF ODATA API with a bearer access token generated by the find destination API but only when using the SAP IAS business user identity (propagated into the destination service via a user’s JWT token in the x-user-token header). However, if using a the same SFSF BizX business user passed directly to destination service either directly via SystemUser property or with manually created user’s JWT wrapper, the generated bearer access token allows for calling into inbound SFSF ODATA APIs without error. So the question quickly turned out to be what is the saml assertion generated with dynamic user identity represented by user JWT token and what it looks like when user identity is static and represented by the SystemUser property or a JWT wrapper ? Let’s see how we can answer that question. |
Here go two destination definitions: the first one is OAuth2SAMLBearerAssertion and the latter is SAMLAssertion.
Please note:
|
OAuth2SAMLBearerAssertion destination definition (retrieves a bearer access token):
{
"Name" : "ACME-OAuth2SAMLBearerAssertion",
"Type" : "HTTP",
"URL" : "https://api2preview.sapsf.eu:443",
"Authentication" : "OAuth2SAMLBearerAssertion",
"ProxyType" : "Internet",
"KeyStorePassword" : "<KeyStorePassword>",
"audience" : "www.successfactors.com",
"XFSystemName" : "<XFSystemName>",
"companyId" : "<companyId>",
"authnContextClassRef" : "urn:oasis:names:tc:SAML:2.0:ac:classes:PreviousSession",
"apiKey" : "<SFSF OAuth client apiKey>",
"clientKey" : "<SFSF OAuth client apiKey>",
"KeyStoreLocation" : "successfactors.p12",
"nameIdFormat" : "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified",
"x_user_token.jwks_uri" : "https://<identityzone>.authentication.<region>.hana.ondemand.com/token_keys",
"tokenServiceURL" : "https://api2preview.sapsf.eu:443/oauth/token",
"userIdSource" : "user_name"
}
SAMLAssertion destination definition (merely generates a saml assertion):
{
"Name" : "ACME-SAMLAssertion",
"Type" : "HTTP",
"URL" : "https://api2preview.sapsf.eu:443/oauth/token",
"Authentication" : "SAMLAssertion",
"ProxyType" : "Internet",
"KeyStorePassword" : "<KeyStorePassword>",
"audience" : "www.successfactors.com",
"XFSystemName" : "<companyId>",
"companyId" : "<companyId>",
"authnContextClassRef" : "urn:oasis:names:tc:SAML:2.0:ac:classes:PreviousSession",
"apiKey" : "<SFSF OAuth client apiKey>",
"clientKey" : "<SFSF OAuth client apiKey>",
"KeyStoreLocation" : "successfactors.p12",
"nameIdFormat" : "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified",
"x_user_token.jwks_uri" : "https://<identityzone>.authentication.<region>.hana.ondemand.com/token_keys",
"tokenServiceURL" : "https://api2preview.sapsf.eu:443/oauth/token"
}
Both definitions look very much alike the main difference being the “Authentication” and “URL” property values. In other words getting the SAMLAssertion destination for your already working OAuth2SAMLBearerAssertion destination is really this proverbial “piece of cake”. However, please consider:
|
Let’s have a closer look at SAMLAssertion destination.
Here goes a result of a SAMLAssertion find destination REST API call. As you can can see the find destination API echoes the destination definition in the json output. At a closer inspection you will notice that the definition is slightly different from the one above with regard to the user claim. It is not using a default “user_name” type of a claim but an “email” type of a claim. The userSourceId property can be used to tell the destination service which user claim to use. It defaults to “user_name” claim of your JWT token. And in the example below I wanted to use an email address as an JWT user claim. Please note: with an email type of user claim I had to adjust the nameIdFormat property to reflect it. |
Find destination:
{
"owner": {
"SubaccountId": "afbac4de-xxxx-xxxx-xxxx-f1d80ccb9ad4",
"InstanceId": null
},
"destinationConfiguration": {
"Name": "ACME-mySAMLAssertion",
"Type": "HTTP",
"URL": "https://api2preview.sapsf.eu:443/oauth/token",
"Authentication": "SAMLAssertion",
"ProxyType": "Onpremise",
"KeyStorePassword": "<KeyStorePassword>",
"userSourceId": "email",
"audience": "www.successfactors.com",
"XFSystemName": "<companyId>",
"companyId": "<companyId>",
"authnContextClassRef": "urn:oasis:names:tc:SAML:2.0:ac:classes:PreviousSession",
"apiKey": "<SFSF apiKey>",
"clientKey": "<SFSF apiKey>",
"KeyStoreLocation": "successfactors.p12",
"nameIdFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
"x_user_token.jwks_uri": "https://<identityzone>.authentication.<region>.hana.ondemand.com/token_keys",
"tokenServiceURL": "https://api2preview.sapsf.eu:443/oauth/token"
},
"certificates": [
{
"Name": "successfactors.p12",
"Content": "MIIQSwIBAzCCEAQGCSqGSIb3DQEHAaCCD/UEgg/xMIIP7TCCCfkGCSqGSIb3DQEHAaCCCeoEggnmMIIJ4jCCCd4GCyqGSIb3WksuH9BFWNLdtLAwqDph7ph9yG083YHYilA0IDb4s4h5cWhJVwTA+MCEwCQYFKw4DAhoFAAQUamZrU984O3v0ug9vbKnwAMnb6PMEFNHdn912ouAzXgcrD0scUuNcvnHHAgMBhqA=",
"Type": "CERTIFICATE"
}
],
"authTokens": [
{
"type": "SAML2.0",
"value": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDI6QXNzZXJ0aW9uIHhtbG5zOnNhbWwyPSJ1cm466dHlwZT0ieHNkOnN0cmluZyI+Q29tcGV0ZW5jeSBNYXRyaXggVXNlcjwvc2FtbDI6QXR0cmlidXRlVmFsdWU+PC9zYW1sMjpBdHRyaWJ1dGU+PC9zYW1sMjpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PC9zYW1sMjpBc3NlcnRpb24+",
"http_header": {
"key": "Authorization",
"value": "SAML2.0 PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDI6QXNzZXJ0aW9uIHhtbG5zOnNhbWwyPSJ1cm46bdHlwZT0ieHNkOnN0cmluZyI+Q29tcGV0ZW5jeSBNYXRyaXggVXNlcjwvc2FtbDI6QXR0cmlidXRlVmFsdWU+PC9zYW1sMjpBdHRyaWJ1dGU+PC9zYW1sMjpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PC9zYW1sMjpBc3NlcnRpb24+"
}
}
]
}
Here goes the decoded SAML assertion (pretty printed):
<?xml version="1.0"?>
<saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsd="http://www.w3.org/2001/XMLSchema" ID="4904a84f-cf16-4a7a-b690-903363b08262" IssueInstant="2021-07-02T07:09:18.722Z" Version="2.0">
<script/>
<saml2:Issuer>successfactors</saml2: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="#4904a84f-cf16-4a7a-b690-903363b08262">
<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#">
<ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="xsd"/>
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>ZDOFMKXBjvwqu0X19iVTSxkEuw7QfVaf0WuOv7zYR4w=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue> MHD0KMyDiDUZ6owkcLaCbD8xsU8FGdhua5CKkXE1w99cYziebDcGwwIzLQjBTxoNfbQajanoy88g aYpB9BNPaNfl90fvZ6THlMSNjKoeZKaL6xVq7lC632pan5pWui4AdTMW8jvBkssGJSyKdbGbLNc= </ds:SignatureValue>
</ds:Signature>
<saml2:Subject>
<saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">firstname.lastname@acme.com</saml2:NameID>
<saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml2:SubjectConfirmationData NotOnOrAfter="2021-07-02T08:09:18.722Z" Recipient="https://api2preview.sapsf.eu:443/oauth/token"/>
</saml2:SubjectConfirmation>
</saml2:Subject>
<saml2:Conditions NotBefore="2021-07-02T06:09:18.722Z" NotOnOrAfter="2021-07-02T08:09:18.722Z">
<saml2:AudienceRestriction>
<saml2:Audience>www.successfactors.com</saml2:Audience>
</saml2:AudienceRestriction>
<saml2:OneTimeUse/>
</saml2:Conditions>
<saml2:AuthnStatement AuthnInstant="2021-07-02T07:09:18.722Z">
<saml2:AuthnContext>
<saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PreviousSession</saml2:AuthnContextClassRef>
</saml2:AuthnContext>
</saml2:AuthnStatement>
<saml2:AttributeStatement>
<saml2:Attribute Name="client_id">
<saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:string"><apikKey></saml2:AttributeValue>
</saml2:Attribute>
<saml2:Attribute Name="api_key">
<saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:string"><apiKey></saml2:AttributeValue>
</saml2:Attribute>
<saml2:Attribute Name="user_uuid">
<saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:string"><user_uuid></saml2:AttributeValue>
</saml2:Attribute>
<saml2:Attribute Name="xs.rolecollections">
<saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:string"><role name></saml2:AttributeValue>
</saml2:Attribute>
</saml2:AttributeStatement>
</saml2:Assertion>
Please pay close attention to the AttributeStatement section from the saml assertion.
As a reminder: user_uuid and xs.rolecollections properties are part of the user JWT token that we passed in the x-user-token header of find destination API call.
And we can see that in addition to api_key and client_id, the destination service populated the user_uuid and a role from xs.rolecollections property array as depicted below:
<saml2:AttributeStatement>
<saml2:Attribute Name="client_id">
<saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:string"><apikKey></saml2:AttributeValue>
</saml2:Attribute>
<saml2:Attribute Name="api_key">
<saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:string"><apiKey></saml2:AttributeValue>
</saml2:Attribute>
<saml2:Attribute Name="user_uuid">
<saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:string"><user_uuid></saml2:AttributeValue>
</saml2:Attribute>
<saml2:Attribute Name="xs.rolecollections">
<saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:string"><role name></saml2:AttributeValue>
</saml2:Attribute>
</saml2:AttributeStatement>
Good to know:
- If you needed any other custom property (attribute) to be populated into the saml bearer assertion as an attribute you would include it in the respective attributes array for instance:
"xs.user.attributes": {},
"xs.system.attributes": {
"xs.rolecollections": [
"<role>"
]
},
Last but not least, let’s see what would happened if we had passed a self-issued user identity.
As aforementioned, the SystemUser property is not supported by SAMLAssertion destination. Thus let’s create manually a user JWT token with the email address of the business user and let’s pass that JWT token in the x-user-token header of the find destination API. (You will find an example how to do it in the following blog post.) The generated saml assertion will only have the api_key and client_id attributes. (I do not include it here for the sake of brevity.) |
Conclusion
Bingo, It looks like the property user_uuid (from SAP IAS) is allegedly mapped into SFSF’s personGuid when calling the SFSF OAuth client token endpoint. And somehow this results in this login error on SFSF ODATA API call side. And that’s all we know for now. |
Good to know:
- The very same troubleshooting approach can be applied regardless of the type of the destination.
- You can use this method with S/4HANA Cloud. S/4HANA and SAP ECC, SAP HANA XSA, CF XSUAA, SuccessFactors, SAC CF and all the commerce LOBs
__________
Salut Piotr, merci pour le lien sur les BTP Kernel Services, je faisais une recherche sur Google pour avoir des infos et je suis tombe sur ton blog... et bravo pour tout les blogs! je vois que tu t'interesse un peu au meme sujets que moi, j'en lirai un de temps en temps ++
Salut Sylvain, en effet j'ai acqui pas mal d'expertise dans le domaine d'integration des solutions business de SAP en outilisant les services techniques et metiers que la BTP offre... et je souhaitais partager cet expertise avec la communaute SAP...merci et a+
Hi Piotr,
Thank you for this great article. We have being trying for several weeks to move from basic auth to OAuth2SAMLBearerAssertion.
We have followed the official documentation (https://help.sap.com/docs/CP_CONNECTIVITY/cca91383641e40ffbe03bdc78f00f681/c69ea6aacd714ad2ae8ceb5fc3ceea56.html?locale=en-US), this blog... and we are always getting the same error: "Authentication credentials are required. Please provide a valid username, password and company id". With Auth2SAMLBearerAssertion username and password should not even be required, any idea whay might be the problem?
Thank you for your help,
Hi Carlos Lopez, can you share your destination definition ? kind regards; Piotr
PS. To help you with the OAuth2SAMLBearerAssertion challenge with SFSF you may find the other articles I wrote on this topic grouped in this intro blog post: https://blogs.sap.com/2021/04/12/sap-successfactors-integration-with-oauth2samlbearerassertion-flow./
Hi Piotr Tesny ,
Thank you for the other blog! I have also followed but still faving same error... This is our definition, do you see anything wrong? I created a ker store location, but is it really required?
Thanks,
Carlos
Hello,
Can you please confirm the nameIdFormat property value is as follows:
How do you test the above destination ? Can you please explain ? And please provide the detailed payload of the find destination run.
Please also note that SAP BTP offers an automation service which can create a communication channel between SFSF and a sub-account using either CF or Kyma runtime environments. It is called
Extending SAP SuccessFactors in the Cloud Foundry and Kyma Environment | SAP Help.
I hope that helps; kind regards; Piotr
PS. What is your relationship with SAP ?