DEV Community

Nicholas Irving
Nicholas Irving

Posted on

How to emulate CIBA with ForgeRock Access Manager

We had a need to create a Customer Service Representative Demo to showcase how to use ForgeRock Access Manager aka PingAM, and we followed the guide at https://backstage.forgerock.com/docs/idcloud/latest/am-oidc1/openid-connect-backchannel-request-flow.html to configure our instance. However our problem is that we dont have access to configure the Push Notification Service, so what we decided to do was mock those interfaces using Scripted Decision Nodes.

Here are the steps

Create Scripted Decision Nodes with Next Generation - JavaScript.

We created 2 Scripts.

  • CIBA-PushSender
  logger.warn("CIBA-PushSender: {}", "Sent");
  action.goTo("Sent");
Enter fullscreen mode Exit fullscreen mode
  • CIBA-PushResultVerifierNode
   logger.warn("CIBA-PushResultVerifierNode: {}", "Success");
   action.goTo("Success");
Enter fullscreen mode Exit fullscreen mode

Created a cibaInit Journey

A journey named cibaInit was created that replicated the example provided by ForgeRock, but using Scripted Decision Nodes

Image description

Node Configuration
Username Collector Image description
Push Sender Image description
Pushed Result Verifier Node Image description
Polling Wait Node Image description

Created a BackChannel Application

Using https://mkjwk.org/ we used the following config

Image description

This was then used to configure an Application called BackChannel

Image description

Testing

Now we have all the basics in place we start testing the solution. In essence everything in the cibaInit journey will just passthrough to get the Access Token, so there should be no delays or issues.

Request a auth-req-id

This requires a JWt Signed by the keys supplied to FRAM.
In Postman we use the following Pre-request script

var jwtPrivateKey = `-----BEGIN PRIVATE KEY-----
....
-----END PRIVATE KEY-----`;

// Set headers for JWT
var header = {
    'alg': 'ES256',
    'typ': 'JWT',
    'kid': 'myCibaKey'
};

// Prepare timestamp in seconds
var currentTimestamp = Math.floor(Date.now() / 1000);


var payload = {
  "aud": "https://xxxx.xxxx.darkedges.com/openam/oauth2",
  "binding_message": "Allow ExampleBank to transfer £50 from 'Main' to 'Savings'? (EB-0246326)",
  "acr_values": "email",
  "exp":  currentTimestamp + 60 * 5,
  "iss": "BackChannel",
  "login_hint": "<email address of user>",
  "scope": "openid profile"
};

function generateJwt() {
    eval(pm.globals.get('jsrsasign-js')); // import javascript jsrsasign

    var sHeader = JSON.stringify(header);
    var sPayload = JSON.stringify(payload);

    var signedToken = KJUR.jws.JWS.sign(header.alg, sHeader, sPayload, jwtPrivateKey);

    pm.environment.set('signed-jwt', signedToken);
    console.log('jwt', signedToken);
}

var navigator = {}; // fake a navigator object for the lib
var window = {}; // fake a window object for the lib

if (pm.globals.has('jsrsasign-js')) generateJwt();
else pm.sendRequest(
    'https://kjur.github.io/jsrsasign/jsrsasign-all-min.js',
    function (err, res) {
        if (err) {
            console.log(err);
        } else {
            pm.globals.set('jsrsasign-js', res.text());
            generateJwt();
        }});
Enter fullscreen mode Exit fullscreen mode

Then it creates the following request

curl --location curl --location 'https://xxxx.xxxx.darkedges.com/openam/oauth2/bc-authorize' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Authorization: ••••••' \
--data-urlencode 'request=eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Im15Q2liYUtleSJ9.eyJhdWQiOiJodHRwczovL3h4eHgueHh4eC5kYXJrZWRnZXMuY29tL29wZW5hbS9vYXV0aDIiLCJiaW5kaW5nX21lc3NhZ2UiOiJBbGxvdyBFeGFtcGxlQmFuayB0byB0cmFuc2ZlciDCozUwIGZyb20gJ01haW4nIHRvICdTYXZpbmdzJz8gKEVCLTAyNDYzMjYpIiwiYWNyX3ZhbHVlcyI6ImVtYWlsIiwiZXhwIjoxNzIxMDg1ODQ2LCJpc3MiOiJCYWNrQ2hhbm5lbCIsImxvZ2luX2hpbnQiOiI8ZW1haWwgYWRkcmVzcyBvZiB1c2VyPiIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUifQ.BA-FkcK4c6J8Heh7dxT6RJLmW6HTe4FZ7b2KSRcFiOFQhp18y_9Taquj0LRNcDBz8w_1qHejvfEAWCEtv8ZosQ'
Enter fullscreen mode Exit fullscreen mode

Which returns

{
    "auth_req_id": "TNrRNWXB86jFthHehaBfyinMpYI",
    "expires_in": 600,
    "interval": 2
}
Enter fullscreen mode Exit fullscreen mode

Get Access Token

Now we can get the Access Token for the request.

curl --location 'https://xxxx.xxxx.darkedges.com/openam/oauth2/access_token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Authorization: ••••••' \
--data-urlencode 'auth_req_id=TNrRNWXB86jFthHehaBfyinMpYI' \
--data-urlencode 'grant_type=urn:openid:params:grant-type:ciba'
Enter fullscreen mode Exit fullscreen mode

which returns

{
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIodXNyITxlbWFpbCBhZGRyZXNzIG9mIHVzZXI-KSIsImN0cyI6Ik9BVVRIMl9TVEFURUxFU1NfR1JBTlQiLCJhdWRpdFRyYWNraW5nSWQiOiJjMGZlNzcwMi02NTE1LTRhMjgtYjFkNi1kNjMxZTRhYjcxNjAtMTM5MjIiLCJzdWJuYW1lIjoiPGVtYWlsIGFkZHJlc3Mgb2YgdXNlcj4iLCJpc3MiOiJodHRwczovL3h4eHgueHh4eC5kYXJrZWRnZXMuY29tL29wZW5hbS9vYXV0aDIiLCJ0b2tlbk5hbWUiOiJhY2Nlc3NfdG9rZW4iLCJ0b2tlbl90eXBlIjoiQmVhcmVyIiwiYXV0aEdyYW50SWQiOiJzVWZHUE1ZMUpjV3J6NVVsWVhHTnhnTGs5c28iLCJhdWQiOiJCYWNrQ2hhbm5lbCIsIm5iZiI6MTcyMTA4NTYzOCwiZ3JhbnRfdHlwZSI6InVybjpvcGVuaWQ6cGFyYW1zOmdyYW50LXR5cGU6Y2liYSIsInNjb3BlIjpbIm9wZW5pZCIsInByb2ZpbGUiXSwiYXV0aF90aW1lIjoxNzIxMDg1NjM4LCJyZWFsbSI6Ii9jb25uZWN0aWQiLCJleHAiOjE3MjEwODkyMzgsImlhdCI6MTcyMTA4NTYzOCwiZXhwaXJlc19pbiI6MzYwMCwianRpIjoiQ1dPTXh5UEpxUzdFR2huNWxzemJjYWF1SlVrIn0.eVG8sBmJY6BvROzDTOjKfGTXzK8SdcYMWbLanrLCgUk",
    "scope": "openid profile",
    "id_token": "eyJ0eXAiOiJKV1QiLCJraWQiOiI4dEZoOURsSnI4SU56OTl6VjBCU094UkdieE09IiwiYWxnIjoiSFMyNTYifQ.eyJhdF9oYXNoIjoiMkJWeDJYZ2Y1UmZJdlhnaEpWVlVpQSIsInN1YiI6Iih1c3IhPGVtYWlsIGFkZHJlc3Mgb2YgdXNlcj4pIiwiYXVkaXRUcmFja2luZ0lkIjoiYzBmZTc3MDItNjUxNS00YTI4LWIxZDYtZDYzMWU0YWI3MTYwLTEzOTIzIiwic3VibmFtZSI6IjxlbWFpbCBhZGRyZXNzIG9mIHVzZXI-IiwiaXNzIjoiaHR0cHM6Ly94eHh4Lnh4eHguZGFya2VkZ2VzLmNvbS9vcGVuYW0vb2F1dCIsInRva2VuTmFtZSI6ImlkX3Rva2VuIiwiZ2l2ZW5fbmFtZSI6IkRlbW8iLCJhdWQiOiJCYWNrQ2hhbm5lbCIsImF6cCI6IkJhY2tDaGFubmVsIiwiYXV0aF90aW1lIjoxNzIxMDg1NjM4LCJuYW1lIjoiRGVtbyBVc2VyIiwicmVhbG0iOiIvY29ubmVjdGlkIiwiZXhwIjoxNzIxMDg5MjM4LCJ0b2tlblR5cGUiOiJKV1RUb2tlbiIsImlhdCI6MTcyMTA4NTYzOCwiZmFtaWx5X25hbWUiOiJVc2VyIn0.7YxVoJ7s-enmQrbNduOQ5Aq57LAi5XVeHApvlzAOJnU",
    "token_type": "Bearer",
    "expires_in": 3599
}
Enter fullscreen mode Exit fullscreen mode

Introspect the Access Token

We can now finally introspect the Access Token

curl --location 'https://fram.connectid.darkedges.com/openam/oauth2/introspect' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Authorization: ••••••' \
--data-urlencode 'token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIodXNyITxlbWFpbCBhZGRyZXNzIG9mIHVzZXI-KSIsImN0cyI6Ik9BVVRIMl9TVEFURUxFU1NfR1JBTlQiLCJhdWRpdFRyYWNraW5nSWQiOiJjMGZlNzcwMi02NTE1LTRhMjgtYjFkNi1kNjMxZTRhYjcxNjAtMTM5MjIiLCJzdWJuYW1lIjoiPGVtYWlsIGFkZHJlc3Mgb2YgdXNlcj4iLCJpc3MiOiJodHRwczovL3h4eHgueHh4eC5kYXJrZWRnZXMuY29tL29wZW5hbS9vYXV0aDIiLCJ0b2tlbk5hbWUiOiJhY2Nlc3NfdG9rZW4iLCJ0b2tlbl90eXBlIjoiQmVhcmVyIiwiYXV0aEdyYW50SWQiOiJzVWZHUE1ZMUpjV3J6NVVsWVhHTnhnTGs5c28iLCJhdWQiOiJCYWNrQ2hhbm5lbCIsIm5iZiI6MTcyMTA4NTYzOCwiZ3JhbnRfdHlwZSI6InVybjpvcGVuaWQ6cGFyYW1zOmdyYW50LXR5cGU6Y2liYSIsInNjb3BlIjpbIm9wZW5pZCIsInByb2ZpbGUiXSwiYXV0aF90aW1lIjoxNzIxMDg1NjM4LCJyZWFsbSI6Ii9jb25uZWN0aWQiLCJleHAiOjE3MjEwODkyMzgsImlhdCI6MTcyMTA4NTYzOCwiZXhwaXJlc19pbiI6MzYwMCwianRpIjoiQ1dPTXh5UEpxUzdFR2huNWxzemJjYWF1SlVrIn0.eVG8sBmJY6BvROzDTOjKfGTXzK8SdcYMWbLanrLCgUk
Enter fullscreen mode Exit fullscreen mode

returns

{
    "active": true,
    "scope": "openid profile",
    "realm": "/connectid",
    "client_id": "BackChannel",
    "user_id": "<email address of user>",
    "username": "<email address of user>",
    "token_type": "Bearer",
    "exp": 1721089238,
    "sub": "(usr!nirving@darkedges.com)",
    "iss": "https://xxxx.xxxx.darkedges.com/openam/oauth2",
    "subname": "<email address of user>",
    "authGrantId": "sUfGPMY1JcWrz5UlYXGNxgLk9so",
    "auditTrackingId": "c0fe7702-6515-4a28-b1d6-d631e4ab7160-13922",
    "expires_in": 3356
}
Enter fullscreen mode Exit fullscreen mode

Final Thoughts

For a Proof of Concept this works for us and should give you the basics for other projects or to demonstrate how it works.

You can download the Journey and OAuth2Client Amster from https://gist.github.com/darkedges/e42a70b4c5a76e817154caafb5b7eb92

Top comments (0)