Device Authorization Flow
La plupart des flux OAuth supposent que l’utilisateur est sur un appareil avec un navigateur web et un clavier pour saisir ses credentials. Cette supposition s’effondre pour :
- Smart TV et lecteurs multimédias : Interface télécommande, pas de saisie texte facile
- Outils CLI : Opèrent dans un terminal, pas de navigateur intégré
- Appareils IoT : Microcontrôleurs sans couche UI
- Consoles de jeux : Interfaces limitées sans accès navigateur
Le Device Authorization Flow (RFC 8628) résout ce problème en laissant l’utilisateur s’authentifier sur un appareil secondaire (son smartphone ou ordinateur) pendant que l’appareil principal attend.
Le Flux
┌─────────────────┐ ┌─────────────────┐
│ Appareil CLI │ │ Serveur Auris │
│ (ou Smart TV) │ │ │
└────────┬────────┘ └────────┬────────┘
│ │
│ 1. POST /api/oauth/device │
│ client_id=xyz&scope=openid │
│─────────────────────────────────────>│
│ │
│ 2. device_code, user_code, │
│ verification_uri, expires_in │
│<─────────────────────────────────────│
│ │
│ 3. Afficher: "Aller sur │
│ accounts.altovar.net/activate │
│ Entrer le code: WDJB-MJHT" │
│ │
│ 4. Poll POST /api/auth/token │
│ (toutes les interval secondes) │
│─────────────────────────────────────>│
│ │
│ [Utilisateur s'authentifie │
│ sur son téléphone/ordi] │
│ │
│ 5. 200 OK avec access_token │
│<─────────────────────────────────────│Étape 1 : Demander les Codes Appareil
POST /api/oauth/device
Content-Type: application/x-www-form-urlencoded
client_id=CLI_CLIENT_ID&scope=openid+profile+emailÉtape 2 : Réception des Codes
{
"device_code": "GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS",
"user_code": "WDJB-MJHT",
"verification_uri": "https://auth.yourdomain.com/activate",
"verification_uri_complete": "https://auth.yourdomain.com/activate?user_code=WDJB-MJHT",
"expires_in": 600,
"interval": 5
}device_code: Utilisé par l’appareil pour le polling — ne jamais l’afficher à l’utilisateuruser_code: Court et lisible — affiché à l’utilisateur pour le saisir sur l’appareil secondaireverification_uri: URL où l’utilisateur va s’authentifierverification_uri_complete: URL avec le code pré-rempli (pour les QR codes)expires_in: Secondes avant expiration des deux codes (600 = 10 minutes)interval: Secondes minimum entre les tentatives de polling (5 secondes)
Étape 3 : Afficher les Instructions à l’Utilisateur
L’appareil affiche les instructions claires :
Auris CLI — Connexion Requise
Ouvre ton navigateur sur :
https://auth.yourdomain.com/activate
Entre le code :
WDJB-MJHT
En attente d'autorisation... (expire dans 10 minutes)Étape 4 : Polling du Token Endpoint
Pendant que l’utilisateur s’authentifie sur son appareil secondaire, l’appareil principal poll le token endpoint :
POST /api/auth/token
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:device_code
&device_code=GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS
&client_id=CLI_CLIENT_IDRéponses possibles pendant le polling :
| Réponse | Action |
|---|---|
error: authorization_pending | Continuer le polling |
error: slow_down | Augmenter l’intervalle de 5 secondes |
error: expired_token | Recommencer le flux depuis l’étape 1 |
error: access_denied | L’utilisateur a refusé — arrêter |
200 OK avec access_token | Succès — stocker les tokens |
Étape 5 : Succès
{
"access_token": "eyJhbGciOiJFUzI1NiJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "8xLOxBtZp8",
"scope": "openid profile email",
"id_token": "eyJhbGciOiJSUzI1NiJ9..."
}Exemple TypeScript — CLI Tool
async function loginDevice(clientId: string): Promise<TokenSet> {
// Étape 1 : Demander les codes appareil
const deviceRes = await fetch('https://auth.yourdomain.com/api/oauth/device', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
client_id: clientId,
scope: 'openid profile email offline_access',
}),
})
const { device_code, user_code, verification_uri, expires_in, interval } =
await deviceRes.json()
// Étape 2 : Afficher les instructions
console.log(`\nOuvre : ${verification_uri}`)
console.log(`Entre le code : ${user_code}\n`)
// Étape 3 : Polling
const deadline = Date.now() + expires_in * 1000
let pollInterval = interval * 1000
while (Date.now() < deadline) {
await new Promise((resolve) => setTimeout(resolve, pollInterval))
const tokenRes = await fetch('https://auth.yourdomain.com/api/auth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
device_code,
client_id: clientId,
}),
})
const data = await tokenRes.json()
if (tokenRes.ok) return data
if (data.error === 'slow_down') {
pollInterval += 5000
} else if (data.error === 'access_denied') {
throw new Error('Autorisation refusée par l\'utilisateur')
} else if (data.error === 'expired_token') {
throw new Error('Code expiré. Réessaie.')
}
// authorization_pending → continuer le polling
}
throw new Error('Timeout — code expiré')
}Design du User Code
Les user codes doivent être courts et faciles à taper sur des interfaces difficiles (télécommande TV, clavier virtuel). Auris génère des codes avec :
- Alphabet de 20 caractères : Pas de voyelles (évite les mots offensants accidentels), pas de caractères ambigus (
0/O,1/I/l) - 8 caractères avec un tiret au milieu (
WDJB-MJHT) pour la lisibilité - Espace de 25,6 milliards de possibilités — difficile à brute-forcer dans la fenêtre de 10 minutes
Le device_code interne est stocké comme hash SHA-256, jamais en clair.
Comparaison des Grant Types
| Grant Type | Initié par | Appareil secondaire | Usage Principal |
|---|---|---|---|
| Authorization Code + PKCE | Utilisateur (navigateur) | Non | Web apps, mobile apps |
| Device Flow | Appareil | Oui | CLI, Smart TV, IoT |
| Client Credentials | Serveur | Non | Services M2M |
| CIBA | Serveur/Agent | Oui (push) | Call center, terminaux POS |
Concepts Associés
- OAuth 2.0 & OIDC — Les concepts d’autorisation sous-jacents
- CIBA — Authentification backchannel initiée côté serveur
- Tokens — Access tokens, refresh tokens et leurs propriétés
- PKCE Flow — Le flux pour web apps et mobile