Skip to Content
Technical Articles

How to generate SAML bearer assertion token for OAuth2SAMLBearerAssertion flow?

Abstract.

This instalment is to help understand what it takes to implement the OAuth2SAMLBearerAssertion Flow without SAP BTP Destination service. Or, in other words, how to manually generate your own SAML Bearer Assertion token.

Albeit that’s not the recommended approach there is still some popular DIY demand.

Disclaimer.

  • As with any DIY work you are on your own when it comes to the nitty-gritty details. The below code snippets are provided as is.
  • From my recent DIY experience: replacing a corroded kitchen sink tap that looked like a simple 30 minutes job turned out to be a serious piece of work even for an experienced professional handy man.

Good to know:

  • This approach is officially documented in the SAP SuccessFactors help pages.

Deja vu.

 

I already covered the OAuth2SAMLBearerAssertion flow with the following mini series of blogs, where the SAP BTP Destination Service acts both as the trusted IdP and a user propagation broker for the OAuth2SAMLBearerAssertion flow, namely:

 OAuth2SAMLBearerAssertion with SAP Analytics Cloud CF edition.
 OAuth2SAMLBearerAssertion with SAP SuccessFactors.

 

But what, if for any reason, you would not want to rely on SAP BTP Destination Service and rather do it on your own? 

Putting it all together.

Per aspera ad astra.

Generate your own OAuth2-SAML-Bearer-Assertion token.

From my own DIY experience that’s the hardest part of the job.

Even, if this is not a recommended approach, doing this will definitely help you understand the OAuth2SAMLBearerAssertion flow mechanism at all and ultimately and very likely make you appreciate the value proposition of SAP BTP Destination service.

SAP SuccessFactors brief.

The public SAP SFSF documentations offers a thorough guidance on how to implement Authentication Using OAuth 2.0, namely:

and describes the following three options to generate a SAML assertion:

 

a. Use a third-party IdP that you trust. 
That's the recommended approach.
Like for instance using SAP BTP Destination Service as our trusted IdP (Identity Provider).

b. Use the offline SAML bearer assertion generation tool.
see SAP note - 3031657

c. Use built-in SAP SuccessFactors IdP SAML bearer 
assertion generation endpoint.
This option is the least recommended as you will need to provide 
the private key of your certificate in the call to the above 
endpoint.


Good to know:

  • You may also refer to these two excellent external blogs covering SAP SuccessFactors and SAP Jam OAuth2 configuration with SAML authentication, namely:
SAP SuccessFactors SAML Authentication in Python
SAP Jam SAML Authentication Using Python

 

SAP Analytics Cloud brief.

 

The access to resources and assets of SAP Analytics Cloud tenant 
is protected with SAML2.0 protocol with the so-called SP-initiated 
WebSSO flow. 
This flow requires a user to enter her or his credentials every 
time the SAML assertion has expired 
(or has been removed from the browser cache).

Also SAC tenant imposes a time limit on the duration 
of the HTTP session.

While this is OK for the interactive usage of SAC tenant this 
may be not OK if a custom client application needed remote 
access to SAC resources or assets without user interaction. 
Indeed, there will be no one to acknowledge and dismiss the 
SAML assertion login pop-up window etc...

This is where the SAP Analytics Cloud App Integration with 
OAuth 2.0 using the SAML bearer assertion flow 
comes into the scene.

 

Arguably there is not much insight across the SAP help pages on how this can be done with the likes of SAP Analytics Cloud. Let’s try to bridge this gap.

OAuth2SAMLBearerAssertion Flow with manually signed SAML Bearer Assertion token.

 

Disclaimer. The below code snippets are provided as is.

 

The plan for this brief is to cover the steps c, d and e, namely:

a. Configuring SAP Analytics Cloud for OAuth2 authentication 
with SAML bearer assertion flow - already covered here.

b. Generating SAML bearer assertions offline: 
Using our own XML-signing code based on 
the following SAP note - 3031657

c. Generating SAML bearer assertions manually [in nodejs]

d. Obtaining a bearer access token.

e. Authenticating SAC API requests.

 

ad c). Generating SAML bearer assertions manually [in nodejs]

Let’s create a simple nodejs generateSAMLBearerAssertion function:

//
// Create SAML assertions. Supports SAML 1.1 and SAML 2.0 tokens.
//
var saml = require('saml').Saml20; // https://www.npmjs.com/package/saml

async function generateSAMLBearerAssertion() {

const  cert = '-----BEGIN CERTIFICATE-----\nMIIFGzCCAwMCBGBb1dwwDQYJKoZIhvcNAQELBQAwUjELMAkGA1UEBhMCVVMxDDAK\nBgNVBAoMA1NBUDEVMBMGA1UECwwMY
..........................(truncated)............................
i\ne3pZsV0QGgSCMZ8kNQobunEPnfkXysLhUvWzniY0UI9uLY7F9934p3PLnZAJOhLJ\nO0X6cHCFbMC+6GxXTdisQVivIOKUURdaHVX6B270SUDiP6TDPApn9E+IaISzPRpk\nXT6c0QNVYg37DBU/qhSN\n-----END CERTIFICATE-----\n'; 
const key = '-----BEGIN PRIVATE KEY-----\nMIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDFz/eQv30tj5oC\nLjT1Im7OtVAVo6mB/wQbEpbOh3LSI
..........................(truncated)............................
JGRqkeRIArXvScbLwq62ViESgOIOU8TdR0n3fachXehZLgRUTa2IGI6zKuVSaXLq\nWBgr0UKz5CLYl4kvZ8ECFbb/I8psoa5LSxBTGdiZznqgLKnImxU1WDSA2xlKJy7J\nAwx8lLYgANSJ7qkKPgPR/t5ZHrx/plY=\n-----END PRIVATE KEY-----\n';


var options = {
  cert: Buffer.from(cert, 'utf-8'), // x509 certificate
  key: Buffer.from(key, 'utf-8'),   // private key (it contains public key as well)

  issuer: 'quovadis/ateam-isveng', // CN of the public certificate key which is the Provider Name in a Trusted SAML Provider in SAC/System/Administration/App Integration
  lifetimeInSeconds: 3600,
  attributes: {
    'client_id': '<OAuth client id from SAC/System/Administration/App Integration>',
  },
  includeAttributeNameFormat: true, //false,
  sessionIndex: '_faed468a-15a0-4668-aed6-3d9c478cc8fa', 
  authnContextClassRef: 'urn:oasis:names:tc:SAML:2.0:ac:classes:PreviousSession',
  nameIdentifierFormat: 'urn:oasis:names:tc:SAML:2.0:attrname-format:email', // urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified
  nameIdentifier: '<user name>', // user email address
  recipient: '<OAuth2SAML token URL from SAC/System/Administration/App Integration>',
  audiences: '<Audience from SAC/System/Administration/App Integration>>',
  // signatureAlgorithm: rsa-sha256',
  // digestAlgorithm: 'sha256',
  signatureNamespacePrefix: 'ds',
};

var unsignedAssertion = saml.createUnsignedAssertion(options);

var signedAssertion =   saml.create(options);
signedAssertion = btoa(signedAssertion);
console.log('btoa-ed signedAssertion: ', signedAssertion);
signedAssertion = encodeURIComponent(signedAssertion);

console.log('unsignedAssertion: ', unsignedAssertion);
console.log('signedAssertion: ', signedAssertion);
return signedAssertion; // unsignedAssertion;
}

 

// this is to create a base64 and URL encoded saml bearer assertion:
//
var samlassertion = await generateSAMLBearerAssertion();

------------------------------------------------------------------------------------
PHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIFZlcnNpb249IjIuMCIgSUQ9Il81TEdreTZjVVF6UXVlWWFnb3BITkVIZXh6RkZnVmNuSiIgSXNzdWVJbnN0YW50PSIyMDIxLTA0LTA1VDA5OjE4OjM5LjIwM1oiPjxzYW1sOklzc3Vlcj5xdW92YWRpcy9hdGVhbS1pc3Zlbmc8L3NhbWw6SXNzdWVyPjxkczpTaWduYXR
.................................... (trucated)......................................
M2E4OC00YmYwLWI4ODYtZWI5ZWM3YWY2OThhIWIxNjU4fGNsaWVudCFiNjU1PC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU%2BPC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48L3NhbWw6QXNzZXJ0aW9uPg%3D%3D

Good to know:

  • You can decode it with any online decoder like for instance SAML Decoder from SSOCircle .

 

 

ad d). Obtaining a bearer access token.

then you can use the above SAML assertion to generate your bearer access token to unlock the SAC APIs by calling the OAuth2SAML token URL endpoint as depicted in the below nodejs snippet:

 

async function getoauthaccesstoken(saml_bearer_assertion) {  

const credentials = { // sacee
    client: { 
        id: '<OAuth client_id>',
        secret: '<OAuth secret>' 
    },
    auth: {
        tokenHost: '<OAuth2SAML token URL>', 
    },
    options: {
        authorizationMethod: 'body'
    }
}

    const options = {
    headers: { "Authorization": "Basic <base64-encoded <client_id>:<secret>:>" }
    
    //headers: { 'Authorization': 'Basic ' + btoa('<client_id>:<secret>') }
    };    

    var params = new URLSearchParams();
    params.append('client_id', credentials.client.id);
    //params.append('client_secret', credentials.client.secret);
    params.append('grant_type',  "urn:ietf:params:oauth:grant-type:saml2-bearer");
    params.append("assertion", saml_bearer_assertion);
    params.append("scope", "openid uaa.user");

  let documents;
  try {
    const response = await axios.post(credentials.auth.tokenHost, params , options);
    documents = JSON.stringify(response.data, null, 2);
    console.log(documents);
    console.log(response.status);

  }
  catch(error) {
      console.log(error.message);
      documents = JSON.stringify(error, null, 2);
  };
  
  return documents;
}
https://localhost:8080/getoauthaccesstoken?assertion=PHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24lPjwvc2FtbDpBdHRyaWJ1dGU%2BPC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48L3NhbWw6QXNzZXJ0aW9uPg%3D%3D

 

and here goes the resulting (truncated) bearer access token:

{
  "access_token": "eyJhbGciOiJSUzI1NiIsImprdSI6Imh0dHBzOi8vYXRlYW0taXN2ZW5nLmF1dGhlbnRpY2F0aW9uLnVzMTAuaGFuYS5vbmRltp6vkKH0PrZvg4e0otB59iG9MvCHV3mMEWzIxccIxrgEcem8_53l3fwzvMA5M",
  "token_type": "bearer",
  "refresh_token": "97c6b547a5ff4a40axxxxxxxxxxxxxx",
  "expires_in": 3599,
  "scope": "openid uaa.user",
  "jti": "c903263a1bdb411595aa103xxxxxxx"
}

 

ad e). Authenticating SAC API requests.

and last but not least it is  time to consume the bearer access token for instance to embed a story in an iframe or get access to the list of stories your user has access to…

best wishes

Piotr Tesny

Additional resources.

SAP Analytics Cloud REST API.

The SAP Analytics REST API gives us access to different assets in the SAC tenant .

The SAC APIs but one are based on the  RESTFUL API standard. Which means these API can be called from any programming language, framework or LCNC application securely over HTTPS.

Extending SAP Solutions Using Manual Configurations

Be the first to leave a comment
You must be Logged on to comment or reply to a post.