A couple of days ago, a good colleague of mine mentioned an OAuth flow that I hadn’t heard about. It was “Client- Initiated Back-Channel Authentication” or CIBA. I thought it was something worth researching, so now I’m gonna share what I learned, and I hope you enjoy it ;)
Think about how you normally log into a website using the normal OAuth flow (which you see as the “Login with Google” Button). You click “Log In”; your browser redirects you to the Google login page; you type your credentials, and then you get redirected back. This is what we know as the Authorization Code Flow.
This works fine on a laptop or a phone. But what happens if you are using a Smart TV or talking to a customer service agent over the phone? Typing a password with a TV remote has a very bad UX, and telling an agent your password over the phone is a huge security risk.
This is exactly the problem CIBA solves. It allows an app to log you in or approve a transaction completely in the background, without forcing your screen to redirect anywhere.
What Exactly is CIBA?
When I first heard about CIBA and read more about it, I thought: “Wait, isn’t this just like a standard phone push notification in MFA (like logging in with Google Authenticator)?”
Not quite. With normal push notifications, you are already trying to log in on a browser. The browser handles the setup, and the push notification is just a second step to prove it’s you.
CIBA splits the process across two entirely different devices:
The Request Device (Consumption Device): The device you are standing in front of (like a Smart TV or a bank counter terminal). It doesn’t even need a web browser or a keyboard.
The Authentication Device: Your personal smartphone. Instead of redirecting your screen, the Smart TV talks directly to the login server in the background (“Hey, user John wants to log in”). The server sends a push notification straight to your phone. You tap “Approve” on your phone, and the TV instantly logs you in.
Two Real-World Examples
To understand the CIBA flow, I analysed a couple of real-world examples to see what’s happening. This method helps me understand the whole philosophy and the step-by-step details of anything I learn. So I thought, let’s check out some examples before we dive into the theoretical flow of CIBA.
1. The Smart TV Upgrade
You are watching a streaming app on your TV and want to buy the Premium plan. Instead of typing in your credit card and password with your TV remote, you hit “Upgrade.”
The TV screen says, “Check your phone.” Your phone gets a pop-up: “Spend $14.99 on the TV app?”
You use Face ID, tap approve, and your TV screen instantly unlocks the Premium content.
So the step-by-step flow of what’s happening here is something like this:
1. You select “Upgrade to Premium” on your Smart TV.
2. The TV app (the Client / Consumption Device) sends a request to your Auth provider with your identifier (login_hint).
3. Your TV displays: “Please check your smartphone (Auth Device) to approve this purchase.”
4. Simultaneously, a push notification appears on your phone: “Authorize $15.99 payment for Premium on ‘Living Room TV’?”
5. You tap “Confirm” on your phone.
6. The TV app, which has been polling the server in the background, receives the “Success” signal and instantly unlocks your content.

Visual Representation of CIBA Flow on Smart TV
2. Calling Your Bank Support
You call your bank to change your home address. To prove who you are, the support agent doesn’t ask for your mother’s maiden name. Instead, they click a button on their computer. A notification pops up on your bank’s phone app asking, “Are you on the phone with an agent right now?” You scan your fingerprint to confirm, and the agent’s computer screen instantly unlocks so they can update your address safely.
Again, this is the flow (with a little bit more detail):
1. Initiation: The customer states their request over the phone. The agent inputs the customer’s username into their terminal. The internal terminal application (acting as the Confidential Client/Consumption Device) makes a backend POST request to the Identity Provider’s /bc-authorize endpoint.
2. The Handshake: The IdP instantly returns an auth_req_id (a temporary transaction token) and an execution interval (e.g., 5 seconds) to the agent’s terminal. The terminal goes into a headless state, background polling the /oauth/token endpoint.(repeatedly calling the Auth Server)
3. Out-of-Band Push: The IdP broadcasts a cryptographic push notification directly to the user’s mobile banking app on their smartphone (the Authentication Device).
4. User Verification: The customer’s phone vibrates mid-call. The screen displays:
”Are you speaking with a support agent right now? Verify the binding code on your screen matches what the agent tells you: Agent ID #402 “
5. Biometric Consent: The user confirms the spoken ID matches, hits “Approve”, and satisfies a biometric prompt (FaceID/Fingerprint).
6. Token Delivery: On the next background poll iteration, the agent’s terminal receives the valid Access and ID tokens from the server. The terminal interface automatically unlocks, granting the agent authorized access to modify the customer’s account records.

Visual Diagram of Bank Support Example
The Core CIBA Flow
Alright, now that we've gotten familiar with CIBA and its use cases, it’s time to look at the core CIBA flow and get into the technical details of each step. Below, there’s a really great diagram by Auth0.com that demonstrates the entire flow.

A Great CIBA Flow Diagram by Auth0.com
Here is the complete main flow of CIBA. In this breakdown, we will look at how it works by itself, using standard permissions (scopes) without the extra complexity of Rich Authorization Requests (RAR).
CIBA focuses purely on a decoupled login. The goal is simple: a client application wants to log a user in, but it wants the user to verify their identity on their smartphone instead of a web browser.
Step 1: The App Initiates the Request
The Request App or Consumption Client (like a Smart TV app) wants to log you in. It makes a direct, backend server-to-server POST request to the login server's /bc-authorize endpoint.
POST /bc-authorize HTTP/1.1
Host: identity-provider.com
Content-Type: application/x-www-form-urlencoded
client_id=smart_tv_app_01
&client_secret=super_secret_backend_key
&[email protected]
&scope=openid profile email
&binding_message=4492login_hint: Tells the server who is trying to log in (in this case, John's email).scope: Standard OAuth permissions. It asks for basic identity details (openid profile email).binding_message: A simple 4-digit code generated by the TV to show on both screens to prevent fraud.
Step 2: The Login Server Acknowledges
The login server verifies that the TV app is legitimate. It looks up John’s account, finds the smartphone registered to his name, and tells the TV app: “Got it. I am working on it. Here is your tracking ID.”
The server responds with a JSON payload:
{
"auth_req_id": "11aa22bb-33cc-44dd",
"expires_in": 120,
"interval": 5
}auth_req_id: The temporary tracking token for this login attempt.interval: Tells the TV app to wait 5 seconds between checks so it doesn't crash the server.
Step 3: The Out-of-Band Notification
Simultaneously, the login server bypasses the TV entirely. It sends a secure, out-of-band push notification directly to the authenticator or banking app on John’s smartphone.
Step 4: User Verification
John’s phone vibrates. He opens the app and sees a straightforward verification message:
Login Request: Are you trying to log into the Smart TV App?
Please confirm the code matches: 4492
[ Approve ] [Deny]
John checks the TV screen, sees the numbers match, and clicks Approve (satisfying a face or fingerprint scan if required). The phone app sends this confirmation back to the login server.
Step 5: The Polling Loop Ends and Tokens are Issued
While John was looking at his phone, the TV app was checking in with the server every 5 seconds behind the scenes:
POST /oauth/token HTTP/1.1
Host: identity-provider.com
Content-Type: application/x-www-form-urlencoded
grant_type=urn:openid:params:grant-type:ciba
&client_id=smart_tv_app_01
&client_secret=super_secret_backend_key
&auth_req_id=11aa22bb-33cc-44ddBefore Approval: The server replies with
{"error": "authorization_pending"}.After John Taps Approve: The next time the TV app checks in, the server replies with an HTTP 200 OK and hands over the standard identity tokens:
{
"access_token": "eyJhbGci...",
"id_token": "eyJteXNh...",
"token_type": "Bearer",
"expires_in": 3600
}The TV app consumes these tokens, learns that John Doe is successfully verified, and instantly logs him into his profile on the screen.
With that, the whole flow is completed !!
Supercharging CIBA with “Rich” Requests
Normal logins just say: “Can John log in?” But what if you are trying to transfer money or buy a subscription? You need to see the details.
CIBA can also work with a feature called RAR (Rich Authorization Requests).
Instead of sending a generic approval text, it sends a highly detailed data to your phone.
If you are transferring money, your phone screen won’t just say “Approve login?” It will show a clean, easy-to-read summary:
Confirm Transfer
From: Savings Account
To: Acme Corporation
Amount: $500.00
Match Code: TV-99
You only hit approve if the “Match Code” on your phone matches the code shown on the TV or terminal screen. This stops hackers from tricking you into approving transactions you didn’t start.
So the only difference with the normal flow is in the initial POST Request to the /bc-authorize endpoint.
POST /oauth/bc-authorize HTTP/1.1
Host: identity.enterprise-bank.com
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
client_id=fintech_ledger_backend
&login_hint={"format":"iss_sub","iss":"https://identity.bank.com/","sub":"usr_99210"}
&binding_message=TXN-4491
&requested_expiry=120
&authorization_details=[{
"type": "openbanking_payment",
"actions": ["initiate_transfer"],
"locations": ["https://api.bank.com/payments"],
"charge_details": {
"amount": "12500.00",
"currency": "EUR",
"debtor_account": "DE-8830219402",
"creditor_account": "FR-1102931102",
"beneficiary_name": "Acme Global Corp"
}
}]As you can see, there is a new authorization_details field in the request body, which specifies the details of the request.
Summary
Look, CIBA combined with RAR is a massive step forward for zero-trust security architectures. But the biggest takeaway here is that CIBA is not meant to replace everything else. You shouldn’t throw away standard flows like Authorization Code with PKCE for your everyday web apps, because it’s not a replacement for the other flows.
Instead, think of CIBA as a highly specialized tool for specific edge cases and some special scenarios. It’s perfect for when you’re dealing with “headless” setups — like smart TVs, kiosk screens, or talking to customer support over the phone — where jumping through a browser redirect just isn’t possible. When you use it for the right job, it elegantly fixes the headache of disconnected devices and makes the security flow incredibly smooth for the user.
Thanks for the reading, guys. I hope you enjoyed it ;)