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");
-
CIBA-PushResultVerifierNode
logger.warn("CIBA-PushResultVerifierNode: {}", "Success"); action.goTo("Success");
Created a cibaInit Journey
A journey named cibaInit
was created that replicated the example provided by ForgeRock, but using Scripted Decision Nodes
Node | Configuration |
---|---|
Username Collector | |
Push Sender | |
Pushed Result Verifier Node | |
Polling Wait Node |
Created a BackChannel Application
Using https://mkjwk.org/ we used the following config
This was then used to configure an Application
called BackChannel
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(); }});
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'
Which returns
{ "auth_req_id": "TNrRNWXB86jFthHehaBfyinMpYI", "expires_in": 600, "interval": 2
}
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'
which returns
{ "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIodXNyITxlbWFpbCBhZGRyZXNzIG9mIHVzZXI-KSIsImN0cyI6Ik9BVVRIMl9TVEFURUxFU1NfR1JBTlQiLCJhdWRpdFRyYWNraW5nSWQiOiJjMGZlNzcwMi02NTE1LTRhMjgtYjFkNi1kNjMxZTRhYjcxNjAtMTM5MjIiLCJzdWJuYW1lIjoiPGVtYWlsIGFkZHJlc3Mgb2YgdXNlcj4iLCJpc3MiOiJodHRwczovL3h4eHgueHh4eC5kYXJrZWRnZXMuY29tL29wZW5hbS9vYXV0aDIiLCJ0b2tlbk5hbWUiOiJhY2Nlc3NfdG9rZW4iLCJ0b2tlbl90eXBlIjoiQmVhcmVyIiwiYXV0aEdyYW50SWQiOiJzVWZHUE1ZMUpjV3J6NVVsWVhHTnhnTGs5c28iLCJhdWQiOiJCYWNrQ2hhbm5lbCIsIm5iZiI6MTcyMTA4NTYzOCwiZ3JhbnRfdHlwZSI6InVybjpvcGVuaWQ6cGFyYW1zOmdyYW50LXR5cGU6Y2liYSIsInNjb3BlIjpbIm9wZW5pZCIsInByb2ZpbGUiXSwiYXV0aF90aW1lIjoxNzIxMDg1NjM4LCJyZWFsbSI6Ii9jb25uZWN0aWQiLCJleHAiOjE3MjEwODkyMzgsImlhdCI6MTcyMTA4NTYzOCwiZXhwaXJlc19pbiI6MzYwMCwianRpIjoiQ1dPTXh5UEpxUzdFR2huNWxzemJjYWF1SlVrIn0.eVG8sBmJY6BvROzDTOjKfGTXzK8SdcYMWbLanrLCgUk", "scope": "openid profile", "id_token": "eyJ0eXAiOiJKV1QiLCJraWQiOiI4dEZoOURsSnI4SU56OTl6VjBCU094UkdieE09IiwiYWxnIjoiSFMyNTYifQ.eyJhdF9oYXNoIjoiMkJWeDJYZ2Y1UmZJdlhnaEpWVlVpQSIsInN1YiI6Iih1c3IhPGVtYWlsIGFkZHJlc3Mgb2YgdXNlcj4pIiwiYXVkaXRUcmFja2luZ0lkIjoiYzBmZTc3MDItNjUxNS00YTI4LWIxZDYtZDYzMWU0YWI3MTYwLTEzOTIzIiwic3VibmFtZSI6IjxlbWFpbCBhZGRyZXNzIG9mIHVzZXI-IiwiaXNzIjoiaHR0cHM6Ly94eHh4Lnh4eHguZGFya2VkZ2VzLmNvbS9vcGVuYW0vb2F1dCIsInRva2VuTmFtZSI6ImlkX3Rva2VuIiwiZ2l2ZW5fbmFtZSI6IkRlbW8iLCJhdWQiOiJCYWNrQ2hhbm5lbCIsImF6cCI6IkJhY2tDaGFubmVsIiwiYXV0aF90aW1lIjoxNzIxMDg1NjM4LCJuYW1lIjoiRGVtbyBVc2VyIiwicmVhbG0iOiIvY29ubmVjdGlkIiwiZXhwIjoxNzIxMDg5MjM4LCJ0b2tlblR5cGUiOiJKV1RUb2tlbiIsImlhdCI6MTcyMTA4NTYzOCwiZmFtaWx5X25hbWUiOiJVc2VyIn0.7YxVoJ7s-enmQrbNduOQ5Aq57LAi5XVeHApvlzAOJnU", "token_type": "Bearer", "expires_in": 3599
}
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
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": "([email protected])", "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
}
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
Discover more from Coursity
Subscribe to get the latest posts sent to your email.