CIBA (Client Initiated Backchannel Authentication)
La plupart des flux d’authentification supposent que l’utilisateur initie l’authentification. Il ouvre une application, clique sur “Se connecter”, et s’authentifie lui-même.
CIBA (Client Initiated Backchannel Authentication) est conçu pour les scénarios où l’authentification est initiée par quelqu’un ou quelque chose autre que la personne qui s’authentifie :
- Centre d’appels : Un agent téléphonique déclenche l’authentification de l’appelant
- Terminal de paiement : Le terminal initie l’autorisation de paiement sur le téléphone du client
- Enceinte intelligente : “Alexa, transfère 200 euros” → challenge d’authentification envoyé au téléphone
- Kiosque : Le kiosque initie le check-in avec authentification sur le téléphone du client
- Autorisation de virement bancaire : La banque envoie une notification push pour confirmer une transaction
CIBA vs Device Flow
Bien que les deux impliquent un appareil secondaire, ils sont fondamentalement différents :
| Device Flow | CIBA | |
|---|---|---|
| Initié par | L’appareil (l’utilisateur voit un code) | Le serveur/agent (l’utilisateur reçoit une notification) |
| Interface utilisateur | Entre un code sur un appareil secondaire | Approuve/refuse une notification push |
| Exige que l’utilisateur | Visite une URL et entre un code | Ait l’app mobile Auris ou une app avec notification push |
| Cas d’usage typique | CLI, Smart TV | Call center, POS, IoT |
Le Flux CIBA
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Agent Call │ │ Serveur Auris │ │ Téléphone │
│ Center │ │ │ │ Utilisateur │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
│ 1. POST /ciba │ │
│ login_hint: alice@... │ │
│ binding_message: │ │
│ "Appel client #1234" │ │
│─────────────────────>│ │
│ │ │
│ 2. auth_req_id │ │
│<─────────────────────│ │
│ │ │
│ │ 3. Push notification │
│ │ "Agent demande accès │
│ │ pour Appel #1234" │
│ │─────────────────────>│
│ │ │
│ 4. Poll token │ 4. Utilisateur │
│ endpoint │ approuve/refuse │
│─────────────────────>│<─────────────────────│
│ │ │
│ 5. access_token │ │
│<─────────────────────│ │Étape 1 : Initier l’Authentification Backchannel
POST /api/oauth/ciba
Content-Type: application/x-www-form-urlencoded
Authorization: Basic BASE64(client_id:client_secret)
[email protected]
&scope=openid+profile+phone
&binding_message=Appel+client+%231234+-+Agent+Bob
&requested_expiry=300
&client_notification_token=NOTIFICATION_TOKENParamètres :
| Paramètre | Description |
|---|---|
login_hint | Identifie l’utilisateur à authentifier (email, user ID, ou téléphone) |
scope | Scopes OAuth demandés |
binding_message | Texte court affiché à l’utilisateur pour lier la requête à sa session |
requested_expiry | Secondes avant expiration de la requête auth (max 600) |
client_notification_token | Token opaque pour la notification côté client (mode Ping/Push) |
Étape 2 : Réponse Initiale
{
"auth_req_id": "1c266114-a1be-4252-8ad1-04986c5b9ac9",
"expires_in": 300,
"interval": 5
}Étape 3 : Notification Push à l’Utilisateur
Auris envoie une notification push à l’appareil mobile de l’utilisateur via son app Auris (ou ton app si tu as intégré le SDK). La notification affiche le binding_message.
Message de liaison et sécurité. Le binding_message est crucial pour prévenir les attaques de “confused deputy” — où un utilisateur est trompé en approuvant une requête pour une session différente de celle qu’il croit autoriser. Rends-le spécifique : “Agent Bob, Appel Client #1234, 15:23” est mieux que “Demande de connexion”.
Ne jamais inclure des données sensibles (mots de passe, numéros de carte) dans le binding_message — il est visible sur l’écran de verrouillage.
Étape 4 : Polling (Mode Poll)
En mode Poll, le client poll le token endpoint pendant que l’utilisateur est en cours d’authentification :
POST /api/auth/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic BASE64(client_id:client_secret)
grant_type=urn:openid:params:grant-type:ciba
&auth_req_id=1c266114-a1be-4252-8ad1-04986c5b9ac9Réponses possibles :
| Réponse | Action |
|---|---|
authorization_pending | Continuer le polling |
slow_down | Augmenter l’intervalle de 5 secondes |
access_denied | L’utilisateur a refusé — arrêter |
expired_token | Requête expirée — informer l’agent |
200 OK avec tokens | Succès |
Les Trois Modes de Notification
Mode Poll (Défaut)
Le plus simple. Le client poll le token endpoint jusqu’à ce que l’utilisateur approuve ou refuse. Pas de callback côté serveur nécessaire.
Avantage : Simple à implémenter. Inconvénient : Polling de la bande passante, latence légèrement plus haute.
Mode Ping
Le serveur Auris envoie une notification HTTP POST à l’URL de callback du client quand l’utilisateur s’authentifie. Le client récupère ensuite les tokens.
Auris → POST https://callcenter.example.com/ciba/callback
Body: {"auth_req_id": "1c266114-..."}Le client fait alors exactement un appel au token endpoint (pas de polling).
Avantage : Plus efficace que le polling, toujours simple. Inconvénient : Nécessite une URL de callback accessible depuis internet.
Mode Push
Auris envoie les tokens directement dans la requête de callback — le client n’a même pas besoin d’appeler le token endpoint.
Avantage : Latence minimale, moins d’appels API. Inconvénient : Les tokens transitent vers le callback — l’URL doit être HTTPS sécurisée.
Exemple TypeScript — Call Center
async function authenticateCallerForAgent(
callerEmail: string,
agentId: string,
callId: string
): Promise<{ accessToken: string; callerProfile: UserProfile }> {
// Étape 1 : Initier l'authentification
const cibaRes = await fetch('https://auth.yourdomain.com/api/oauth/ciba', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: `Basic ${btoa(`${CLIENT_ID}:${CLIENT_SECRET}`)}`,
},
body: new URLSearchParams({
login_hint: callerEmail,
scope: 'openid profile phone',
binding_message: `Agent ${agentId} | Appel ${callId} | ${new Date().toLocaleTimeString()}`,
requested_expiry: '120',
}),
})
const { auth_req_id, interval } = await cibaRes.json()
// Notifier l'agent : "Authentification en cours..."
console.log(`Demande d'authentification envoyée à ${callerEmail}`)
// Étape 2 : Polling avec backoff
let pollIntervalMs = interval * 1000
while (true) {
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs))
const tokenRes = await fetch('https://auth.yourdomain.com/api/auth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: `Basic ${btoa(`${CLIENT_ID}:${CLIENT_SECRET}`)}`,
},
body: new URLSearchParams({
grant_type: 'urn:openid:params:grant-type:ciba',
auth_req_id,
}),
})
const data = await tokenRes.json()
if (tokenRes.ok) {
return {
accessToken: data.access_token,
callerProfile: parseIdToken(data.id_token),
}
}
if (data.error === 'slow_down') {
pollIntervalMs += 5000
} else if (data.error === 'access_denied') {
throw new Error('L\'appelant a refusé l\'authentification')
} else if (data.error === 'expired_token') {
throw new Error('Authentification expirée')
}
// authorization_pending → continuer
}
}Concepts Associés
- Device Flow — Quand l’utilisateur initie lui-même (Smart TV, CLI)
- OAuth 2.0 & OIDC — Le cadre d’autorisation sous-jacent
- Tokens — Access tokens, refresh tokens et leurs propriétés
- DPoP — Lier les tokens à une clé cryptographique pour plus de sécurité