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

How to generate SAML bearer assertion token with SAP BTP Destination Service?

We have already seen how to generate a SAML bearer assertion token programmatically with SuccessFactors and Analytics Cloud as well as with S/4HANA on premise

Now it’s time to explore how to offload this task to our polyvalent SAP BTP destination service.

I happened to come across this option a little bit by accident as I was exploring ways to simplify the  OAuth2SAML2 setup with on premise S/4HANA ABAP server…Indeed, SAP BTP destination service is capable of many things, one of them being SAML Assertion Authentication – page 105

What initially misled me was that in order to be able to select the SAML Assertion Authentication one must choose the OnPremise proxy type [in the destination definition]. And I did not want to have to create a virtual mapping to my S/4HANA ABAP server gateway in any CloudConnector.

But, as it  eventually turned out,  it does work without any CloudConnector and is not even calling the destination URL.

Good to know:

  • You can leverage the destination service as a genuine saml bearer assertion generator.


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

Putting it all together.

To make this exercise more relevant I will be replacing the previously discussed programmatically generated saml bearer assertion with the one produced by the destination service.


A. Preparing a destination. Let’s call it SAML-EC_ADM_OAUTH.

  • create a destination definition (either from BTP sub-account UI or with destination service API)
  • Provide x_user_token.jwks_uri property with the URI of the JSON web key set, containing the signing keys which are used to validate the JWT provided in the X-UserToken header.

These are the two main options for getting a user’s JWT token. Additionally, you may need to pass this token in the X-UserToken header of the destination service find API call.


Have the IDP of your ABAP system (QJ9) trust a SAP BTP sub-account.

The BTP sub-account becomes a service provider. Then, when you deploy business applications to any BTP runtime: CF, Kyma etc.

BTP’s XSUAA service – acting as an OIDC provider – will automatically pass your user’s JWT token in every HTTP header back to your application.

Good to know: in this scenario you may not need to pass additionally the JWT token in the X-UserToken header of the destination service find API call.


Alternatively, you may create an OIDC application in the IDP of your ABAP system (QJ9).

Then you can retrieve a user’s JWT token of your currently logged SAML IDP user either implicitly or, explicitly, by calling the OIDC application authentication flow as depicted below:

Here goes an example of a user’s JWT token: eyJraWQiOiJLZ0RMLVQ2NFhuQThoSmFESnlxeGRQZVgwNDgiLCJhbGciOiJSUzI1NiJ9…


As my nodejs client application is not necessarily deployed on BTP I have opted the latter approach.


B. Generating SAML bearer assertion token

  • find destination and retrieve the base64 encoded token
  • decode the token for verification

Here goes a screenshot from API Business Hub which I recommend to all when it comes to prototyping with APIs

and below is the result of the find destination API call

  "owner": {
    "SubaccountId": "xxxxxxxxxx-e2a9-4c17-84ec-xxxxxxxxxx",
    "InstanceId": null
  "destinationConfiguration": {
    "Name": "SAML-EC_ADM_OAUTH",
    "Type": "HTTP",
    "URL": "https://<host>.<domain>:<port>/sap/bc/sec/oauth2/token?sap-client=666",
    "Authentication": "SAMLAssertion",
    "ProxyType": "OnPremise",
    "KeyStorePassword": "<KeyStorePassword>",
    "audience": "QJ9_666",
    "authnContextClassRef": "urn:oasis:names:tc:SAML:2.0:ac:classes:x509",
    "Description": "QUOVADIS-EC_ADM_OAUTH anywhere",
    "KeyStoreLocation": "quovadis_ateam-isveng.p12",
    "clientKey": "EC_ADM_OAUTH",
    "nameIdFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified",
    "CloudConnectorLocationId": "",
    "x_user_token.jwks_uri": "https://<IDP host>.<domain>/oauth2/certs",
    "userIdSource": "<user claim from JWT token>" // defaults to "user_name", 
other values could be "email" or "sub" etc...
  "certificates": [
      "Name": "quovadis_ateam-isveng.p12",
      "Type": "CERTIFICATE"
  "authTokens": [
      "type": "SAML2.0",
      "value": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDI6QXNzZXJ0aW9uIHhtbG5zOnNhbWwyPSJ1cm46jAtODUyZS00ODQzNmZlYjZlNjI8L3NhbWwyOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDI6QXR0cmlidXRlPjwvc2FtbDI6QXR0cmlidXRlU3RhdGVtZW50Pjwvc2FtbDI6QXNzZXJ0aW9uPg==",
      "http_header": {
        "key": "Authorization",
        "value": "SAML2.0 PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDI6QXNzZXJ0aW9uIHhtbG5zOnNhbWwyPSJ1cm46bAtODUyZS00ODQzNmZlYjZlNjI8L3NhbWwyOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDI6QXR0cmlidXRlPjwvc2FtbDI6QXR0cmlidXRlU3RhdGVtZW50Pjwvc2FtbDI6QXNzZXJ0aW9uPg=="

The decoded SAML assertion:

<saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsd="" ID="eb2f1943-6239-4856-a4c0-1110e9222b0e" IssueInstant="2021-06-01T21:45:55.366Z" Version="2.0">
<ds:Signature xmlns:ds="">
<ds:CanonicalizationMethod Algorithm=""/>
<ds:SignatureMethod Algorithm=""/>
<ds:Reference URI="#eb2f1943-6239-4856-a4c0-1110e9222b0e">
<ds:Transform Algorithm=""/>
<ds:Transform Algorithm="">
<ec:InclusiveNamespaces xmlns:ec="" PrefixList="xsd"/>
<ds:DigestMethod Algorithm=""/>
<ds:SignatureValue> Xo1eT8mNPTMbhmIUmJjgKn64Qdo2dqZa061l46s1S4/a1sBxRJNYfFqYR/pskG/cLI38QBewqMiq zodR2DUndgZ8CV/i+P3zs7sfCbM6amQRBK1i7/rPaxfo2hRIEfYky/2YKMGuOltpx9E5ke0PgwrB lkwisTVA+RhN37QCHPSlRZFPZcdTlkDrg3fZNdwbzZ4UtOYgTE1abedS6RbZgPVQP2x9P3ZfbqLR URxawxSBbpv8OL5rDQzTH1dymR8UpftfyuvmVRe9uIAUYtY6cHdxqEoi+KaWMBsWrDOu/2IYCtkv Gij52RSFzmei6SGNjhenKo846+3mZYcxWbwZLNWwDENHbDvlh255fULCBhjEvkIgnMBmjd8jZu4w hI86HqMGwrLyDDwEF9yaznBzOSj4YJ+mOzEb73XPz/XlwX1eJFs9odge5SFv6a1zv1bRk+bvVwm7 rasQXCDhkwY8Q4GWbbLJSg3uQW+TRntSLkzTRtwGa/otZfc1Mpw5V6yM8Bx9W7qR6HgF7oH7AKxy rjhGvEyl2cQwKi1zXIOJ8FALCa1GY7wNBAZKV44Dpbsqq6UXkIE/UB+1qCzJBYktOGyrpLw4c03N 5PqyZncb3Gua1m//0/3GCbrPasTXDmT5ruMTl4y5Mw8hDGgDOURDV2+xd/L5LMZrCnaDG3SA8tA= </ds:SignatureValue>
<saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"><QJ9_user_name></saml2:NameID>
<saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml2:SubjectConfirmationData NotOnOrAfter="2021-06-01T22:45:55.366Z" Recipient="https://<host>.<domain>:<port>/sap/bc/sec/oauth2/token?sap-client=666"/>
<saml2:Conditions NotBefore="2021-06-01T20:45:55.366Z" NotOnOrAfter="2021-06-01T22:45:55.366Z">
<saml2:AuthnStatement AuthnInstant="2021-06-01T21:45:55.366Z">
<saml2:Attribute Name="client_id">
<saml2:AttributeValue xmlns:xsi="" xsi:type="xsd:string">EC_ADM_OAUTH</saml2:AttributeValue>
<saml2:Attribute Name="user_uuid">
<saml2:AttributeValue xmlns:xsi="" xsi:type="xsd:string"><user_uuid from JWT token></saml2:AttributeValue>


Please pay attention to the following destination definition values as to make sure the saml assertion tags will be populated with the correct values as well.

1. the assertion issuer must be equal to OAuth2.0 Identity Provider name on NW ABAP Side (SAML2 tcode).

If the name of OAuth2.0 Identity Provider is not equal the CN name of the x509 certificate then you will need to use an additional destination service property, namely assertionIssuer to give the OAuth2.0 Identity Provider name.

Otherwise the destination service will automatically use the CN name of the x509 trust in use. Please note as I am using my own trust I had to add this trust to the destination service key store and need to use the these two additional properties, namely

KeyStoreLocation and KeyStorePassword

to refer it to the proper x509 trust.


2. nameIdFormat must be either urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified or urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress

QJ9_user_name is either the business or technical user name

<saml2:NameID Format=”urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified“><QJ9_user_name></saml2:NameID>

3. Recipient (ACS – Assertion Consumer Service endpoint), Please note ?sap-client=666 must be added otherwise the ACS will not be recognized and the assertion rejected as invalid

<saml2:SubjectConfirmationData NotOnOrAfter=”2021-06-01T22:45:55.366Z” Recipient=”https://<host>.<domain>:<port>/sap/bc/sec/oauth2/token?sap-client=666“/>

4. audience restriction: this is the name of the LocalProvider on QJ9 side, as depicted below:


5. client_id: this is the name of your OAuth client

<saml2:Attribute Name=”client_id“>
<saml2:AttributeValue xmlns:xsi=”” xsi:type=”xsd:string”>EC_ADM_OAUTH</saml2:AttributeValue>

C. Calling into OAuth2.0 client token endpoint

You can use the SAML Assertion you obtained from the destination service find API call with the OAuth2.0 client token endpoint in order to obtain the bearer access token in turn for the subsequent ODATA calls.

The value of assertion is accessible via authTokens[0].value

  "authTokens": [
      "type": "SAML2.0",
      "value": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDI6QXNzZXJ0aW9uIHhtbG5zOnNhbWwyPSJ1cm46jAtODUyZS00ODQzNmZlYjZlNjI8L3NhbWwyOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDI6QXR0cmlidXRlPjwvc2FtbDI6QXR0cmlidXRlU3RhdGVtZW50Pjwvc2FtbDI6QXNzZXJ0aW9uPg==",
      "http_header": {
        "key": "Authorization",
        "value": "SAML2.0 PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDI6QXNzZXJ0aW9uIHhtbG5zOnNhbWwyPSJ1cm46bAtODUyZS00ODQzNmZlYjZlNjI8L3NhbWwyOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDI6QXR0cmlidXRlPjwvc2FtbDI6QXR0cmlidXRlU3RhdGVtZW50Pjwvc2FtbDI6QXNzZXJ0aW9uPg=="

    const options = {
      auth: {
      password: credentials.client.secret

    headers: { 
        'Accept': 'application/json',

    var params = new URLSearchParams();
    if (scope !== '') params.append("scope", scope); 
    params.append('grant_type',  "urn:ietf:params:oauth:grant-type:saml2-bearer");

  let documents;
  let access_token;
  try {

    const response = await + '?sap-client=' + credentials.options.sap_client, params , options);
    documents = JSON.stringify(, null, 2);
    access_token =;

  catch(error) {
      documents = JSON.stringify(error, null, 2);

As a result we will retrieve the bearer access token as described here.


D. Calling into ODATA API

For further details, please goto the relevant section in my sibling blog post.


Good to know:

If your NW ABAP version has built-in support for SAML Bearer Assertion Provider, you might use the SAML Bearer Assertion in the Authorization header of ODATA calls thus bypassing the OAuth2 client brokering. However in this case the recipient of the assertion must reflect the real ACS endpoint (=the API endpoint) and no longer the OAuth client token endpoint!



As you may have seen using the destination service comes with a certain price. In the end it is yet another tool that you may need to learn first. But certainly, the benefits outweigh the efforts. The less code you have to write and maintain the more time you can spend on the functional aspects of your solution.

Enjoy SAP BTP destination service!

Looking forward to hearing from you. Please post any questions and comments you may have in the add comment section below.



Additional resources

ABAP acting as a Resource Server. App2App integration with OAuth2SAML2BearerAssertion flow.

Simple Five Steps to configure API/ODATA services in the SAP S/4HANA On-Premise back-end system to run SAP Intelligent RPA process bots

Implementing Employee Central Payroll (INTERNAL Login required– Authorized for SAP Customers and Partners)

1688545 – OAuth 2.0 Server in AS ABAP Troubleshooting

Assigned Tags

      You must be Logged on to comment or reply to a post.
      Author's profile photo Shady Shen
      Shady Shen

      Can we config arbitrary saml attribute for the destination?

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

      Hi Shady, I am afraid this is not possible.

      If you needed to configure custom saml assertion attributes you will be better off either doing it manually or using a SAML Assertion policy that SAP publishes on the SAP API Business Hub . I hope that helps; kind regards; Piotr

      Author's profile photo Faisal Jamal
      Faisal Jamal

      Hi Piotr Tesny

      I am looking for an alternate option to get saml assertion to be used in oath authentication from SAP API Management to SuccessFactors. We are on Neo environment. Is it still possible to use the steps in this blog to achieve this?

      I followed this link to implement the policies but later learned that /oauth/idp end point is being deprecated and shoul not be used anymore. Hence, I am wondering if it is possible to use the Destination service to generate saml assertions for SAP APIM.

      Sorry to comment on an old post. 


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

      Hello Faisal Jamal,

      indeed, if you are creating an API proxy with SAP API Management you can use either an APIM built-in SAML assertion policy or a BTP destination...

      You might also want to consider SAP Graph now part of the SAP Integration Suite and a SFSF destination.

      I hope that helps;

      PS. The full line-up of SAP SuccessFactors blogs

      And I am planning to publish a new blog entitled "SAP SuccessFactors APIs easy with SAP API Management and SAP Graph". Watch this space...

      Author's profile photo Faisal Jamal
      Faisal Jamal

      Thank you very much for responding, Piotr Tesny !

      I am looking to use the built in SAML Assertion policy, for which I am following this pdf (Page 232 for SAML Assertion generation) and also the SAP help link. But I have certain doubts regarding this.

      If we use this method, I believe we also have to somehow let SuccessFactors know to accept the assertions generated by SAP APIM policy to issue the access tokens, right? How do we do that?

      Also, where do get the required details to put in SAP APIM policy. Which certificates should be used to create the Keystore in SAP APIM. I have gone through multiple blogs but none of them seems to be giving a clear picture on how to set up the OAuth authentication between SAP APIM and SuccessFactors. I would really appreciate if you could please guide me to some helpful links with all the necessary steps. Thanks in advance!