How to use CIBA with Keycloak casestudies banner image

How to use CIBA with Keycloak

Lets start with what is CIBA?

CIBA: Client Initiated Backchannel Authentication Grant

Clients who wish to start the authentication process by communicating directly with the OpenID Provider instead of going via the user's browser, as with OAuth 2.0's authorization code grant.

Okay, Why do we need it?

Lets take some simple case studies to get a clear picture.

Case Study 1:


Consider BANK-A has CIBA enabled, and Customer-A has an account in BANK-A.

Customer-A is heading to the supermarket to buy groceries, but he has forgotten his wallet and still wants to pay. Since BANK-A is CIBA enabled, Counter Manager obtains Customer-A phone number and initiates payment to BANK-A. BANK-A will then send Customer-A a authorization request, which will be approved or denied, and payment will be deducted from BANK-A to Shop.

Case Study 2:


Consider a call center agent who wishes to use CIBA to authenticate the caller.

Call center agents will seek identification and initiate a CIBA request while waiting for the caller's consent. Once the caller approves, the call center will get the information.

How to implement with Keycloak?

Take a look at the following sequence diagram on CIBA flow

1_YAhX7nihxHTjEuV46ETUCg.png

This requires few steps to be followed.

Step 1:


Download the Sample rest api in nodejs and run the sample code. This will be our external authorization request handler (ExtAuth)

Step 2:


Add the following xml snippet below the theme element in Standalone.xml. If you're running (ExtAuth) on a different port, make the necessary changes in the snippet below.

<spi name="ciba-auth-channel"><default-provider>ciba-http-auth-channel</default-provider><provider name="ciba-http-auth-channel" enabled="true"><properties><property name="httpAuthenticationChannelUri" value="**http://localhost:3001/request**"/></properties></provider></spi>

Step 3:


Note: Step 2 is an important step to make CIBA work.

Login to Keycloak dashboard and go to Authentication -> CIBA Policy

1_i6h9x_0Ogd_IIPy-qkKgpA.png

Set following properties

Backchannel Token Delivery Mode -> poll [Where we poll the token endpoint to see if the result has arrived], [Ping is something where Keycloak will inform the endpoint, when the result arrives]

Expires In -> 120 (Expiry time for our CIBA Request)

Interval -> 5 (Interval between out poll requests)

Authentication Requested User Hint -> login_hint (As of 15.0.2 Keycloak support login_hint)

Click save.

Step 4:


Create a new client and enable the following settings.

Access type -> Confidential

OIDC CIBA Grant Enabled -> true

1_iYm_fdo4n3uSokASSsZGuw.png

Click save

Step 5:


Copy the client id and client secret from the above created client and replace in following places and execute.


curl --location --request POST 'http://localhost:8080/auth/realms/master/protocol/openid-connect/ext/ciba/auth' \\\--header 'Content-Type: application/x-www-form-urlencoded' \\\--data-urlencode 'client\_id=**{client\_id}**' \\\--data-urlencode 'client\_secret=**{client\_secret}**' \\\--data-urlencode 'login\_hint=**{username}**' \\\--data-urlencode 'scope=openid' \\\--data-urlencode 'binding\_message=**{message u wish to show to user}**

Note: login_hint for now works only with username

Following response will be received.


{"auth\_req\_id": "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0..SU2CRJt8Imk\_CHJUvn\_X1Q.2JZ-291bWVGhTTg8wtxzUi5b0u60Mc6jnV\_Vium6af8EvQUMQKuS1VDnRJlJdjvzGlOZlgiFu26QYdfPZXRjdXyX0YQ1CRF7-oDZCHujJxpoXMuZGjdBRg\_1Yx8onF83WWN0KCUj7aPto-W0JXObrN0VUPK8jRzdsgv0pq46G2\_9bJxJjA3hk7s1\_zgXvjF2IUWuHOZhLZWsl9lrtKYTt0J5aK4JNvCSI4myIWvwEwv\_kLTVdiCsb8\_LcSxSVeO84x7PcPqvfminClIFfKtUlZr9K28j0MOUcSwTI5y7vo6pf3jt51kA-UokMW26BKVWjGnogzfTUE0xWVpX-JWQm9tR3iglI9caIjNcW4DJ0gJs\_fMc6UrMoCByxOshcIY9IJP\_Ml8iA0wruRTqxOYRcOZ8fte9PxtgYko7JLIAaCdcGAl089o6lIOouW-uMrpkKShmHznjnm1ZpaDBLVynzAJtHjqFGkqo6cIGHCtuTJab3eN\_EZE-zFsucTT0CLPblJYy391ZaH2hV3sqAs1nJp8PPxiaWYHFhv3N0ol49RLpVVcAHZrXDGZt0GEZyNIqDS58kgVg8v9O97lK0WyQ-2UyWzpSLSQzUVBPBT8SllxRaURsxhbl5Nwj9NCPOkkBeleuTttjf1InVGLUxVORo7PzK-0i3adyP4WdWo1rg2NQNrH1Jm6-sCMMNveFfXWpyLB\_jBdTIu2Lz-D9skBVniFuYGNpqmsMPyc8lj6NPkJVYVQr9dd3A3WkI2xXBeOZ2XwfcBkZ7zFqXa7S9Sc93WSvZW67Mvi4Vbd3CmBtUltWs86KrMVjpjdJOe423c5n17aNSTR\_9jwDleBGbfFpQwjj-jXBVWiY4GBA9wxMn8EUtTa3S4NiObV0V64N9kuzGkUTx9p2YoawlCLWYpfDrBkSblZkO\_dHeXaTQIH-Kw60Qel\_ZVDxX\_60NKakCU93.pwsV9QOEYOtaoWowWH7zAg","expires\_in": 120,"interval": 5}

Check the logs of sample app running from Step 1, the logs will have the authorization header and received a request.


{  
  authorization: 'Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI1aTluNzhOSHZsVkt4VHpWM21TM0dkSkotQnhEVTluWlBaZVN4ZzN3MFlJIn0.eyJleHAiOjE2MzY0MDUwODYsImp0aSI6ImYzNTE1OWVmLTE3MzUtNDVlNy1iY2FlLTQyYTZmOWI2OGViYyIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9hdXRoL3JlYWxtcy9tYXN0ZXIiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvbWFzdGVyIiwic3ViIjoiNzRhZjg0ZDgtMzZlYy00MTdlLThhODQtYmMzNGE4YzU3MzAxIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoidGVzdCJ9.C5ubpbD7tJAbaEBXaje1DGvtfUrwKunh9wZGuQluC1Kq1Mkprn2RT1wL1ph2flC-fdmr3ocMfMDjzAsljwz1aMFY7nGzIcLVXPGy1klcqbrkOT\_GlbK8oCXr5w-x4ruiyWUIJ8BOCknaxc\_fA9PaYepA3Z8r5n3XHazIhivsOEmZDW1JFPmfuYbPrmZqm4cYnTWEL9QMzyWD\_RGFLr3e2Dc0J6iaQi1AAwt3iakaaiqUdUfzBSGwD9H0dRafEOdDnptrKUjNh\_e0kP7paqAsYuYg1RJY34pQF08hAB3S6tIaK5of0gDRGjmJV4SfA\_0IsgBU2N5hQvXjovnhNE69sg',  
  'content-type': 'application/json',  
  'content-length': '188',  
  host: 'localhost:3001',  
  connection: 'Keep-Alive',  
  'user-agent': 'Apache-HttpClient/4.5.13 (Java/11.0.4)',  
  'accept-encoding': 'gzip,deflate'  
}

Step 6:


Start polling to token endpoint


curl --location --request POST 'http://localhost:8080/auth/realms/master/protocol/openid-connect/token' \\\--header 'Content-Type: application/x-www-form-urlencoded' \\\--data-urlencode 'grant\_type=urn:openid:params:grant-type:ciba' \\\--data-urlencode 'auth\_req\_id=**{auth\_req\_id received in Step 5}**' \\\--data-urlencode 'client\_id=**{client\_id}**' \\\--data-urlencode 'client\_secret=**{client\_secret}**

Will get a response as


{"error": "authorization\_pending","error\_description": "The authorization request is still pending as the end-user hasn't yet been authenticated."}

In case if its tried before the interval, following response will receive.

{"error": "slow\_down","error\_description": "too early to access"}

Step 7:


Trigger an Authorization request to the user AD (Authorization Device) , here it shall be any ways to get authorization from the user and it depends upon the business scope.

Following are some of the ways to get authorized.

Send a predefined email link.

Send an otp to phone number.

Trigger an authorization request, with okta, Kobil etc.

Step 8:


Based on the user response from (AD), forward the response to callback channel of Keycloak.


curl --location --request POST 'http://localhost:8080/auth/realms/master/protocol/openid-connect/ext/ciba/auth/callback' \\\--header 'Authorization: **Bearer {Get the Authorization token received at Step 5}**' \\\--header 'Content-Type: application/json' \\\--data-raw '{"status" : "**SUCCEED**" // UNAUTHORIZED, CANCELLED}

Step 9:


After Step 8, polling done in Step 6 will return the access token, id token and refresh token for further processing.


{"access\_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI1aTluNzhOSHZsVkt4VHpWM21TM0dkSkotQnhEVTluWlBaZVN4ZzN3MFlJIn0.eyJleHAiOjE2MzY0MDcyNjMsImlhdCI6MTYzNjQwNzIwMywiYXV0aF90aW1lIjoxNjM2NDA3MjAzLCJqdGkiOiJmMDdlMGRlZi0wY2M3LTRkYzQtYjhhMy00NjkyNzM3OWU1NzIiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvbWFzdGVyIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6Ijc0YWY4NGQ4LTM2ZWMtNDE3ZS04YTg0LWJjMzRhOGM1NzMwMSIsInR5cCI6IkJlYXJlciIsImF6cCI6InRlc3QiLCJzZXNzaW9uX3N0YXRlIjoiZTRjMjRhNDYtMWEwYi00YjVkLWI2MDQtMDM4YzM2ZGM4ZjdmIiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJkZWZhdWx0LXJvbGVzLW1hc3RlciIsIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6Im9wZW5pZCBlbWFpbCBwcm9maWxlIiwic2lkIjoiZTRjMjRhNDYtMWEwYi00YjVkLWI2MDQtMDM4YzM2ZGM4ZjdmIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJqYXJ2aXMifQ.ba1EORuMotg-zmKkaF21Byt7AdBEPBasuTmaOYIMPrHN32FUOVDuUmAQ564mYKr-Xhr2lqzQQTugNcekRvzglbrG4gyrNOcz2snea-TU4gD9JXor0Vg5I-80hzXw8pUg8U9-I-LBnVk9xMQzY9HSbaqMEMgUerBMDgJknm8EwJMok7EY8M0p55VoI89hAN0jmAYfJuMhZWvuUCIXGNvVQiL6kADUMEp2Lls7M5VHRXAcdV-CWI8eMepsBv7DcsypuGiWRlVbpi89VNBnzxpIMx8IlaOEzHBWBuxo4rBK2H2qBLHQc2kAEjhuoOB8mkPvw0fym8gLhJE6CfZdgEs2dA","expires\_in": 60,"refresh\_expires\_in": 1800,"refresh\_token": "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI1YzNkYjdjOC1jMGVjLTRmZGQtYmVmYS02NzllNGJkNjUyMWMifQ.eyJleHAiOjE2MzY0MDkwMDMsImlhdCI6MTYzNjQwNzIwMywianRpIjoiN2Y2MzRmZmUtMDZlMC00Y2Q2LWEyMDMtZjBkNzM5ZmUzNGFkIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL21hc3RlciIsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9hdXRoL3JlYWxtcy9tYXN0ZXIiLCJzdWIiOiI3NGFmODRkOC0zNmVjLTQxN2UtOGE4NC1iYzM0YThjNTczMDEiLCJ0eXAiOiJSZWZyZXNoIiwiYXpwIjoidGVzdCIsInNlc3Npb25fc3RhdGUiOiJlNGMyNGE0Ni0xYTBiLTRiNWQtYjYwNC0wMzhjMzZkYzhmN2YiLCJzY29wZSI6Im9wZW5pZCBlbWFpbCBwcm9maWxlIiwic2lkIjoiZTRjMjRhNDYtMWEwYi00YjVkLWI2MDQtMDM4YzM2ZGM4ZjdmIn0.iCw2YiKXsQ0K7bTNFCUT1xrt-5lxsxT1SPl0RaF2Qvw","token\_type": "Bearer","id\_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI1aTluNzhOSHZsVkt4VHpWM21TM0dkSkotQnhEVTluWlBaZVN4ZzN3MFlJIn0.eyJleHAiOjE2MzY0MDcyNjMsImlhdCI6MTYzNjQwNzIwMywiYXV0aF90aW1lIjoxNjM2NDA3MjAzLCJqdGkiOiI1NTYzNmUyOC1iMzEyLTRkM2YtYTQ2NS0xNTU3NTM1ODgxYzkiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvbWFzdGVyIiwiYXVkIjoidGVzdCIsInN1YiI6Ijc0YWY4NGQ4LTM2ZWMtNDE3ZS04YTg0LWJjMzRhOGM1NzMwMSIsInR5cCI6IklEIiwiYXpwIjoidGVzdCIsInNlc3Npb25fc3RhdGUiOiJlNGMyNGE0Ni0xYTBiLTRiNWQtYjYwNC0wMzhjMzZkYzhmN2YiLCJhdF9oYXNoIjoiZWhReElucnhremtDWDJleVE0S3R6ZyIsImFjciI6IjEiLCJzaWQiOiJlNGMyNGE0Ni0xYTBiLTRiNWQtYjYwNC0wMzhjMzZkYzhmN2YiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6ImphcnZpcyJ9.VMkBAL6W7NUqM\_TocZEwc5jKH\_W91BSa0cdwssp7qxGZG89fcgFurVIfmdIeIQEDMwOL\_GrH\_5IginjJipyoQ6FL5iDgbkVoJDbEEHqz8NHXO1fWrhzcSRIB6JOghrp-d2CCqlMGEgSHzjnJvuEHbfyLK63u0dzxUbLu-psuMH2KGWhsEUQqjDgNYADReafzBANTzExHrXgUY64am9B5L-csJdjWkSgX9UIfvQ5mLVte3gnLQChg-nkiET585uJMG8JEbJG-0BWjJT3bf1htxhZM7ex\_FmkdnKN6VRc42pvsVYXviPbZBLUMRTy88R6mj5E0mgft763HW55gVlJVAA","not-before-policy": 0,"session\_state": "e4c24a46-1a0b-4b5d-b604-038c36dc8f7f","scope": "openid email profile"}

I hope it gives some light on CIBA in Keycloak.

Added Postman Collection here.