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 strong DIY demand. |
Disclaimer.
Good to know:
|
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:
|
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. |
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). Please note that if you are using the destination service trust it will be used to sign the saml assertion. You still will need to somehow provide the user claim for the saml assertion. If using the destination service the recommended approach is to provide the user's JWT token in the X-user-token header of the find destination call. b. Use the offline SAML bearer assertion generation tool. see SAP note - 3031657 or you could create the assertion programmatically as shown below. c. Use built-in SAP SuccessFactors IdP SAML bearer assertion generation endpoint. This option is the least recommended as you would need to provide the private key of your certificate in the call to the above endpoint. If this is the option you need to adopt you might consider using the SAP API Management's SuccessFactor connectivity policy. Good to know:
|
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. |
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
|
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
.................................... (truncated)......................................
M2E4OC00YmYwLWI4ODYtZWI5ZWM3YWY2OThhIWIxNjU4fGNsaWVudCFiNjU1PC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU%2BPC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48L3NhbWw6QXNzZXJ0aW9uPg%3D%3D
Good to know:
|
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 = { // OAuth client_id and secret from SAC OAuth client you have created
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=PHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnNhbWw9InVybjpvYXN...
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"
}
Please note the format of the payload (body) must be set to x-www-form-urlencoded . A fairly common mistake being setting only Content-Type header to application/x-www-form-urlencoded. The signed saml assertion is only valid for one hour. When its validity has expired you would receive the following response from calling into the OAuth recipient { "error": "unauthorized", "error_description": "Error validating SAML message" } |
|
Extending SAP SuccessFactors:
|
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. |
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
5 | |
4 | |
3 | |
2 | |
2 | |
2 | |
2 | |
2 | |
2 | |
2 |