Enterprise Resource Planning Blogs by SAP
Get insights and updates about cloud ERP and RISE with SAP, SAP S/4HANA and SAP S/4HANA Cloud, and more enterprise management capabilities with SAP blog posts.
cancel
Showing results for 
Search instead for 
Did you mean: 
quovadis
Product and Topic Expert
Product and Topic Expert
6,164



















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.






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.



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.

1.








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.

2.








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",
"Content": "MIIQeAIBAzCCEDIGCSqGSIb3DQEHAaCCECMEghAfMIIQGzCCCggGCSqGSIb3DQEHAaCCCfkEggn1MIIJ8TCCCe0GCyqGSIb3PG/HcO5xW0ai3ZkwPTAhMAkGBSsOAwIaBQAEFMyS26dKft/dWRSIVg/SQvDnQmaSBBQTST14Lse+rKA3igKg4Q7gzIB0mAICBAA=",
"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="http://www.w3.org/2001/XMLSchema" ID="eb2f1943-6239-4856-a4c0-1110e9222b0e" IssueInstant="2021-06-01T21:45:55.366Z" Version="2.0">
<script/>
<saml2:Issuer>quovadis/ateam-isveng</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="#eb2f1943-6239-4856-a4c0-1110e9222b0e">
<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>M1JVj0c2Gr6KOz0dkfrwlMUCShGPWcX1c891MeSWqfg=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<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>
</ds:Signature>
<saml2:Subject>
<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:SubjectConfirmation>
</saml2:Subject>
<saml2:Conditions NotBefore="2021-06-01T20:45:55.366Z" NotOnOrAfter="2021-06-01T22:45:55.366Z">
<saml2:AudienceRestriction>
<saml2:Audience>QJ9_666</saml2:Audience>
</saml2:AudienceRestriction>
<saml2:OneTimeUse/>
</saml2:Conditions>
<saml2:AuthnStatement AuthnInstant="2021-06-01T21:45:55.366Z">
<saml2:AuthnContext>
<saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:x509</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">EC_ADM_OAUTH</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 from JWT token></saml2:AttributeValue>
</saml2:Attribute>
</saml2:AttributeStatement>
</saml2:Assertion>

 

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.






<saml2:Issuer>quovadis/ateam-isveng</saml2:Issuer>

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"/>
</saml2:SubjectConfirmation>

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









<saml2:Audience>QJ9_666</saml2:Audience>

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






<saml2:Attribute Name="client_id">
<saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:string">EC_ADM_OAUTH</saml2:AttributeValue>
</saml2:Attribute>


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: {
username: credentials.client.id,
password: credentials.client.secret
},

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

var params = new URLSearchParams();
params.append('client_id', credentials.client.id);
if (scope !== '') params.append("scope", scope);
params.append('grant_type', "urn:ietf:params:oauth:grant-type:saml2-bearer");
params.append("assertion",authTokens[0].value);

let documents;
let access_token;
try {

const response = await axios.post(tokenUrl + '?sap-client=' + credentials.options.sap_client, params , options);
documents = JSON.stringify(response.data, null, 2);
access_token = response.data.access_token;
console.log(access_token);
console.log(documents);
console.log(response.status);

}
catch(error) {
console.log(error.message);
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!

 

Conclusion.










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


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

1688545 - OAuth 2.0 Server in AS ABAP Troubleshooting

5 Comments
shady_shen
Product and Topic Expert
Product and Topic Expert
0 Kudos
Can we config arbitrary saml attribute for the destination?
quovadis
Product and Topic Expert
Product and Topic Expert
0 Kudos
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
former_member440061
Participant
0 Kudos

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. 

Thanks!

quovadis
Product and Topic Expert
Product and Topic Expert
0 Kudos

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 https://blogs.sap.com/tag/quovadis-sfsf/

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

former_member440061
Participant
0 Kudos
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!