Skip to Content
Technical Articles
Author's profile photo Piotr Tesny

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)

    • The native platform services can be consumed from any runtime. In plain English from any type of third-party application/environment. For instance, the Other runtime options allows for consuming native services from anywhere (from other bespoke runtime or environment including your own RaspberryPi:) ).
    • The focus of this instalment is to demonstrate how to 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 JWT token (OIDC identity).

Disclaimer:

  • Please note all the code snippets below are provided “as is”.
  • All the x509 certificates, bearer access and refresh tokens and the likes have been redacted.
  • Images/data in this blog post is from SAP internal sandbox, sample data, or demo systems. Any resemblance to real data is purely coincidental.

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).

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

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:

  • SAMLAssertion destination  executes just part of the flow as compared to OAuth2SAMLBearerAssertion
  • The destination service will merely generate and return a SAML assertion with this kind of destination.

Please note:

  • SAMLAssertion authentication method does not accept a SystemUser property in the destination definition. Thus, if you want to populate your SFSF business user name like for instace sfadmin in the generated saml assertion you will need to create a JWT wrapper to include sfadmin in one of the claims of your JWT token; for instance in the user_name claim.
  • If you are using a BTP sub-account as a service provider together with xsuaa service, your external WebSSO user’s identity (for instance a SAP IAS user) will be translated by xsuaa service, acting as an OIDC provider, into this user JWT token.
  • You may refer to the this community post to gain further insight into how to retrieve a user JWT from a CAP application or to the Token-Based Authentication (JWT) section of the capire manual.

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:

  • In the below destination definitions we are using SFSF tenant DC2 preview URLs. You may need to adjust to yours.
  • For the full list of API url’s used to connect to the SFSF instances located in different Data Centers please refer to 2215682Successfactors API URLs for different Data Centers

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:

  • You must add a valid user JWT token in the x-user-token header with either destination when calling the  find destination REST API

 

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

__________

Additional resources.

What is the correct way to obtain the user JWT and resuse for an HTTP call

CAP: Demystify User Authentication

Easy path to productive use with the free tier model for SAP Business Technology Platform

Best Practices for SAP BTP – SAP Help Portal

Adapt Cloud Samples for Use in an SAP S/4HANA System On Premise | SAP Help

Assigned Tags

      2 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Sylvain Cazenave
      Sylvain Cazenave

      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 ++

       

      Author's profile photo Piotr Tesny
      Piotr Tesny
      Blog Post Author

      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+