View as Markdown

CIBA over WhatsApp — implementation guide

Authenticate a user without leaving the WhatsApp conversation you already have with them — bc-authorize, the WhatsApp approval message, binding_message, delivery modes, and troubleshooting.

How-to

CIBA over WhatsApp — implementation guide

This guide walks through the most common way teams use the CIBA flow: a WhatsApp chatbot or contact-center agent authenticating a user in the middle of a conversation. The user is already chatting with your business on WhatsApp — instead of asking them to switch to a browser, your backend starts a backchannel request and Truora Pass sends the approval into that same WhatsApp thread.

When to use it

  • A WhatsApp conversation is your relying party. Your bot (or a human agent working from a WhatsApp business console) needs to confirm who it’s talking to, or get the user’s consent to pull identity data, without ever leaving WhatsApp. This is the scenario this guide covers end to end.
  • Any other decoupled/backend flow — call centers, kiosks, batch backend jobs — works the same way at the protocol level. See the CIBA flow reference for the full request/response contract this guide builds on.
  • Your user is in your own browser or app instead of WhatsApp? Use the Authorization Code flow or the Embedded SDK — those redirect in-page rather than sending a message to a device.

Prerequisites

  • Your application is registered with the CIBA grant type, urn:openid:params:grant-type:ciba. See Registering your application.
  • The user already has a Truora Pass account whose email matches your login_hint.
  • That account has a verified phone number — this is where the WhatsApp approval message is delivered. A user with no verified phone cannot be reached by this flow (see Troubleshooting).

What the user receives

When your backend calls bc-authorize, Truora Pass sends the user a WhatsApp message built from these pieces of your request:

  • A greeting using the user’s first name.
  • Your application’s name, framed as a request for the user’s approval.
  • Your binding_message as the line describing what is being asked.
  • How long the request stays open, in whole minutes.
  • A link the user taps to open the approval screen, where they sign in (if needed), review what’s being requested, and approve or deny.
Sample WhatsApp approval message (illustrative mock)

The image above is an illustrative mock, not a live capture — it shows how the pieces above come together, not the literal wording of the message your users will see.

Note: today this message is sent in English only, regardless of the user’s locale.

Writing a good binding_message

binding_message is the line the user reads in WhatsApp as the reason for the request — write it so a user can decide, from that line alone, whether to approve. For example: "Share your documents with CapiBank" rather than "Authorization request".

  • It’s shown to the user exactly as you send it.
  • Longer messages are truncated to 200 characters.
  • It is required whenever your request includes a sensitive scope: documents, documents:images, biometric:image, background, or validations:write. Omitting it on a sensitive-scope request is rejected with invalid_request.

See the Scopes reference for the full scope catalog.

End-to-end worked example

Say your WhatsApp bot for CapiBank is chatting with a user, Ana, and needs her to share her verified documents before continuing.

1. Your bot’s backend starts the backchannel request

curl -X POST https://api.pass.truora.com/v1/oauth2/bc-authorize \
  --data-urlencode "client_id=WLT_APP_your_client_id" \
  --data-urlencode "client_secret=your_client_secret" \
  --data-urlencode "scope=openid identity documents" \
  --data-urlencode "login_hint=ana@example.com" \
  --data-urlencode "binding_message=Share your documents with CapiBank" \
  --data-urlencode "requested_expiry=600"
{
  "auth_req_id": "<opaque request identifier>",
  "expires_in": 600,
  "interval": 5
}

2. Ana approves in the same WhatsApp

Truora Pass sends Ana the approval message described above, in the same WhatsApp app she was already chatting with your bot in. She taps the link, signs in if needed, reviews the requested scopes and your binding_message, and approves. Your bot’s UI (or the WhatsApp thread) doesn’t need to do anything during this step.

This is the approval screen Ana lands on:

The Truora Pass approval screen on mobile: the request title, the requesting application, the message, the requested scopes, and the Approve and Deny buttons

3. Your backend receives the result

You have two ways to get the outcome: poll the token endpoint (shown below), or register push delivery so Truora Pass POSTs the token response to a webhook you control the moment Ana decides — see push delivery in the CIBA guide. Polling looks like this:

curl -X POST https://api.pass.truora.com/v1/oauth2/token \
  --data-urlencode "grant_type=urn:openid:params:grant-type:ciba" \
  --data-urlencode "auth_req_id=<auth_req_id from step 1>" \
  --data-urlencode "client_id=WLT_APP_your_client_id" \
  --data-urlencode "client_secret=your_client_secret"

Once Ana approves, this returns tokens:

{
  "access_token": "<access token>",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "openid identity documents",
  "id_token": "<id token, because openid was requested>"
}

4. Your bot reads Ana’s identity

curl https://api.pass.truora.com/v1/oauth2/userinfo \
  -H "Authorization: Bearer <access_token>"

See UserInfo and claims for the full claim map, and the CIBA flow guide for the complete polling-response reference (authorization_pending, access_denied, expired_token, and so on).

Delivery modes

There are two ways the approval link reaches the user:

  1. Truora-delivered WhatsApp message (default) — the flow described above. Truora Pass sends the WhatsApp message on your behalf; you never see the raw approval link.

  2. You deliver the link yourself — for example, in your own WhatsApp bot conversation, an in-app chat, or any other channel you control, instead of Truora sending a WhatsApp message. This isn’t a flag on the plain request above: self-delivery is part of the approval requests (Consent Gate) feature — you send authorization_details describing the prompt (instead of relying on binding_message) and set delegate_notification_to_client=true. Truora Pass then hands you the approval_url directly in the bc-authorize response instead of sending anything.

    Using authorization_details also changes how you get the result: any approval request — self-delivered or not — is not redeemable at the token endpoint the way the example above is; you read the outcome from GET /v1/oauth2/approval-outcome instead. See Approval requests (Consent Gate) for the full request shape, the delegate-mode details, and how outcome retrieval works.

Troubleshooting

Situation What happens What to do
WhatsApp message could not be delivered notifier_unavailable (HTTP 503) at bc-authorize The request that failed to notify is not retryable by auth_req_id — the response never gave you one. Simply call bc-authorize again; the retry creates a brand-new request.
login_hint doesn’t resolve to exactly one account unknown_user_id Confirm the email matches the user’s Truora Pass account exactly, and that it isn’t ambiguous.
Resolved user has no verified phone invalid_request — “the resolved user has no verified phone for delivery” The user must verify a phone number in Truora Pass before this flow can reach them.
Sensitive scope with no binding_message invalid_request Add a binding_message — see Writing a good binding_message.
Choosing requested_expiry Default 300 seconds (5 minutes) if omitted; values above 900 seconds (15 minutes) are capped at 900. Pick a window realistic for a user to notice and act on a WhatsApp message.
Polling too fast No penalty — polling faster doesn’t get you an earlier answer Respect the interval in the bc-authorize response (currently 5 seconds) to avoid unnecessary load.
Message language Sent in English only today, regardless of the user’s locale Don’t assume localized copy when writing user-facing support material.

See Errors and troubleshooting for the full error reference across all Truora Pass endpoints.

See also