Authentification
OAuth 2.0 sur Woupi : code d’autorisation avec PKCE, identifiants client, code d’appareil, actualisation, validation et révocation.
Aperçu
Woupi est un fournisseur OAuth 2.0 (RFC 6749, avec PKCE selon la RFC 7636 et l’octroi par code d’appareil selon la RFC 8628). Votre application obtient des jetons d’accès et les envoie à l’API REST dans l’en-tête Authorization: Bearer, accompagnés de son Client-Id.
| Type de jeton | Obtenu avec | À utiliser pour |
|---|---|---|
| Jeton d’accès utilisateur | Code d’autorisation (+ PKCE), code d’appareil ou le générateur de jetons du portail | Agir au nom d’un utilisateur de Woupi, dans les limites des portées qu’il a accordées : clavardage, paramètres de chaîne, extraits, données privées, abonnements aux événements par WebSocket, drops d’un joueur. |
| Jeton d’accès d’application | client_credentials (applications confidentielles seulement) | Appels de serveur à serveur sans utilisateur : données publiques, abonnements aux événements par webhook, lecture et traitement de tous les droits de drops de votre application. |
| Valeur | Préfixe | Durée de vie |
|---|---|---|
| Secret client | wps_ | Jusqu’à ce que vous le renouveliez |
| Jeton d’accès | wpa_ | 14400 s (4 heures) |
| Jeton d’actualisation | wpr_ | 60 jours, usage unique (remplacé à chaque actualisation) |
| Code d’autorisation | wpc_ | 600 s (10 minutes), usage unique |
| Code d’appareil | wpd_ | 1800 s (30 minutes); le code d’utilisateur associé ressemble à ABCD-EF23 |
Remarque
Traitez les jetons comme des chaînes opaques : ne les analysez jamais et ne présumez pas d’une longueur fixe au-delà du préfixe.
Points de terminaison
| Point de terminaison | Méthode | Rôle |
|---|---|---|
https://woupi.buzz/oauth/authorize | GET (navigateur) | Page de consentement du flux par code d’autorisation. |
https://api.woupi.buzz/oauth2/token | POST | Échanger un code, un jeton d’actualisation, un code d’appareil ou des identifiants client contre des jetons. |
https://api.woupi.buzz/oauth2/device | POST | Démarrer le flux par code d’appareil. |
https://woupi.buzz/activate | GET (navigateur) | Page où les utilisateurs saisissent le code d’un appareil. |
https://api.woupi.buzz/oauth2/validate | GET | Vérifier un jeton et connaître son propriétaire, ses portées et sa durée de vie restante. |
https://api.woupi.buzz/oauth2/revoke | POST | Révoquer un jeton d’accès ou d’actualisation. |
Les points de terminaison /oauth2/token, /oauth2/device et /oauth2/revoke acceptent des corps application/x-www-form-urlencoded ou JSON. Tous les points de terminaison oauth2 envoient les en-têtes CORS.
Flux par code d’autorisation
Utilisez ce flux lorsqu’un utilisateur se connecte à votre site ou à votre application avec Woupi. L’utilisateur approuve votre application dans son navigateur, Woupi le renvoie chez vous avec un code de courte durée, puis votre application échange ce code contre des jetons.
Étape 1 — dirigez l’utilisateur vers la page d’autorisation :
https://woupi.buzz/oauth/authorize
?response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_uri=https%3A%2F%2Fexample.com%2Fcallback
&scope=user%3Aread%3Aemail%20chat%3Aread%20chat%3Awrite
&state=c3f1a9e0b74d2e86
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256
&force_verify=false| Paramètre | Obligatoire | Description |
|---|---|---|
response_type | Oui | Toujours code. |
client_id | Oui | L’identifiant client de votre application. |
redirect_uri | Oui | Doit correspondre exactement à l’un des URI de redirection enregistrés pour l’application. |
scope | Oui | Liste de portées séparées par des espaces (encodés %20). Consultez Portées. |
state | Recommandé | Une valeur impossible à deviner, conservée dans la session de l’utilisateur et vérifiée à son retour. Elle est renvoyée telle quelle. |
code_challenge | Applications publiques : oui | Empreinte SHA-256 de votre code_verifier, encodée en base64url sans remplissage. |
code_challenge_method | Avec code_challenge | Toujours S256. |
force_verify | Non | true affiche l’écran de consentement même si l’utilisateur a déjà accordé toutes les portées demandées. Par défaut : false. |
Étape 2 — le consentement. Si l’utilisateur n’est pas connecté, Woupi lui demande d’abord de se connecter. L’écran de consentement présente l’icône, le nom et le développeur de votre application ainsi que les portées demandées, avec les boutons Autoriser et Annuler. Si l’utilisateur a déjà accordé toutes les portées demandées à votre application et que force_verify ne vaut pas true, Woupi saute cet écran et redirige immédiatement.
Étape 3 — Woupi redirige vers votre redirect_uri avec un code (préfixe wpc_, valide 10 minutes, usage unique), les portées accordées et votre state :
https://example.com/callback?code=wpc_Vd8sK2mQ7xN4pL0rT9yB3hF6jZ1cW5gA&scope=user%3Aread%3Aemail%20chat%3Aread%20chat%3Awrite&state=c3f1a9e0b74d2e86Si l’utilisateur annule ou si la requête est invalide, la redirection transporte un paramètre error au lieu d’un code, accompagné de votre state :
https://example.com/callback?error=access_denied&state=c3f1a9e0b74d2e86error | Signification |
|---|---|
access_denied | L’utilisateur a cliqué sur Annuler. |
invalid_request | Un paramètre est absent ou invalide, par exemple une application publique sans code_challenge. |
invalid_scope | Une portée demandée n’existe pas. |
unauthorized_client | L’application ne peut pas être autorisée pour le moment, par exemple parce que le compte de son développeur n’est pas approuvé. |
Attention
Lorsque le client_id est inconnu ou que le redirect_uri n’est pas enregistré pour l’application, Woupi affiche une page d’erreur et ne redirige jamais, afin de ne pas envoyer les utilisateurs vers une adresse non fiable.
Étape 4 — échangez le code contre des jetons depuis votre serveur. Envoyez exactement le même redirect_uri et, si vous avez envoyé un code_challenge, le code_verifier correspondant :
curl -X POST https://api.woupi.buzz/oauth2/token \
-d grant_type=authorization_code \
-d client_id=YOUR_CLIENT_ID \
-d client_secret=wps_YOUR_CLIENT_SECRET \
-d code=wpc_Vd8sK2mQ7xN4pL0rT9yB3hF6jZ1cW5gA \
-d redirect_uri=https://example.com/callback \
-d code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk{
"access_token": "wpa_3pXq9vN0cT7yLw2KzR5mB8dF1hJ4sG6aE0uI2oY7tQ",
"refresh_token": "wpr_Hk2P9sLx4Tq7Wm1Zc8Vb5Nr0Yd3Gf6Ja2Ue9Io4Kp7",
"expires_in": 14400,
"scope": "user:read:email chat:read chat:write",
"token_type": "bearer"
}Les applications publiques omettent client_secret. Un code ne peut être échangé qu’une seule fois : une deuxième tentative renvoie invalid_grant.
PKCE
PKCE (Proof Key for Code Exchange) lie le code d’autorisation à l’instance de l’application qui a lancé le flux. Il est obligatoire pour les applications publiques et recommandé pour les applications confidentielles. Générez un code_verifier aléatoire pour chaque autorisation, conservez-le (dans la session de l’utilisateur ou en mémoire), envoyez son empreinte S256 comme code_challenge, puis envoyez le vérificateur lui-même lors de l’échange du code.
const crypto = require('crypto');
// One verifier per authorization attempt (43 to 128 URL-safe characters).
const codeVerifier = crypto.randomBytes(32).toString('base64url');
const codeChallenge = crypto.createHash('sha256').update(codeVerifier).digest('base64url');
const state = crypto.randomBytes(16).toString('hex');
// Store codeVerifier and state in the user's session, then redirect:
const params = new URLSearchParams({
response_type: 'code',
client_id: process.env.WOUPI_CLIENT_ID,
redirect_uri: 'https://example.com/callback',
scope: 'chat:read chat:write',
state,
code_challenge: codeChallenge,
code_challenge_method: 'S256',
});
const authorizeUrl = 'https://woupi.buzz/oauth/authorize?' + params.toString().replace(/\+/g, '%20');
// In the callback handler, after checking that req.query.state === session.state:
async function exchangeCode(code, codeVerifier) {
const res = await fetch('https://api.woupi.buzz/oauth2/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
client_id: process.env.WOUPI_CLIENT_ID,
client_secret: process.env.WOUPI_CLIENT_SECRET, // omit for public apps
code,
redirect_uri: 'https://example.com/callback',
code_verifier: codeVerifier,
}),
});
const body = await res.json();
if (!res.ok) throw new Error(body.error + ': ' + body.error_description);
return body; // { access_token, refresh_token, expires_in, scope, token_type }
}Flux par identifiants client
Les applications confidentielles obtiennent un jeton d’accès d’application en s’authentifiant avec leur identifiant et leur secret client. Aucun utilisateur n’intervient et aucun jeton d’actualisation n’est émis : demandez un nouveau jeton lorsque le jeton courant expire.
curl -X POST https://api.woupi.buzz/oauth2/token \
-d client_id=YOUR_CLIENT_ID \
-d client_secret=wps_YOUR_CLIENT_SECRET \
-d grant_type=client_credentials{
"access_token": "wpa_3pXq9vN0cT7yLw2KzR5mB8dF1hJ4sG6aE0uI2oY7tQ",
"expires_in": 14400,
"scope": "",
"token_type": "bearer"
}- Les applications publiques reçoivent
unauthorized_clientpour cet octroi. - Un jeton d’application est obligatoire pour créer des abonnements aux événements par webhook.
- Les points de terminaison qui exigent un utilisateur (par exemple
POST /v1/chat/messages) refusent les jetons d’application avec 401missing user token.
Flux par code d’appareil
Utilisez ce flux sur les appareils où il est peu pratique de saisir un mot de passe ou de suivre une redirection : jeux, consoles, téléviseurs et outils en ligne de commande. Votre application affiche un code court; l’utilisateur le saisit sur son téléphone ou son ordinateur à https://woupi.buzz/activate et y approuve l’application. C’est aussi ainsi que les jeux associent le compte Woupi d’un joueur pour les Drops.
Étape 1 — demandez un code d’appareil :
curl -X POST https://api.woupi.buzz/oauth2/device \
-d client_id=YOUR_CLIENT_ID \
-d "scope=drops:read"{
"device_code": "wpd_Lm4Qx8Ns2Vt6Rb0Yc3Kh7Wp1Jd5Fg9Za",
"user_code": "WDKR-7M3Q",
"verification_uri": "https://woupi.buzz/activate",
"verification_uri_complete": "https://woupi.buzz/activate?code=WDKR-7M3Q",
"expires_in": 1800,
"interval": 5
}Étape 2 — affichez le user_code et le verification_uri à l’utilisateur, par exemple « Rendez-vous à woupi.buzz/activate et saisissez WDKR-7M3Q ». Vous pouvez aussi afficher verification_uri_complete sous forme de code QR : le code est alors prérempli. Gardez le device_code secret sur l’appareil.
Étape 3 — interrogez le point de terminaison de jetons toutes les interval secondes jusqu’à ce que l’utilisateur accepte ou refuse, ou que le code expire après expires_in secondes :
curl -X POST https://api.woupi.buzz/oauth2/token \
-d client_id=YOUR_CLIENT_ID \
-d grant_type=urn:ietf:params:oauth:grant-type:device_code \
-d device_code=wpd_Lm4Qx8Ns2Vt6Rb0Yc3Kh7Wp1Jd5Fg9ZaPendant l’attente, le point de terminaison répond 400 avec une erreur OAuth. Une fois l’autorisation accordée, il renvoie la réponse habituelle avec un jeton d’accès et un jeton d’actualisation, et le code d’appareil ne peut plus servir.
{
"error": "authorization_pending",
"error_description": "The user has not approved the request yet"
}error | Que faire |
|---|---|
authorization_pending | L’utilisateur n’a pas encore terminé. Continuez d’interroger au même intervalle. |
slow_down | Vous avez interrogé avant la fin de interval. Ajoutez 5 secondes à votre intervalle et continuez. |
access_denied | L’utilisateur a refusé. Cessez d’interroger et informez-le. |
expired_token | Le code a expiré (30 minutes). Cessez d’interroger et recommencez à l’étape 1. |
async function pollForToken(clientId, deviceCode, intervalS) {
let waitS = intervalS;
for (;;) {
await new Promise((resolve) => setTimeout(resolve, waitS * 1000));
const res = await fetch('https://api.woupi.buzz/oauth2/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
client_id: clientId,
grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
device_code: deviceCode,
}),
});
const body = await res.json();
if (res.ok) return body; // { access_token, refresh_token, expires_in, scope, token_type }
if (body.error === 'authorization_pending') continue;
if (body.error === 'slow_down') { waitS += 5; continue; }
throw new Error(body.error); // access_denied, expired_token, invalid_grant...
}
}Remarque
Un jeu ou un outil distribué aux joueurs ne peut pas garder un secret : enregistrez-le comme application publique et n’envoyez que client_id. Les applications confidentielles doivent aussi s’authentifier (voir Authentification du client ci-dessous).
Actualiser les jetons
Les jetons d’accès expirent après 4 heures. Utilisez le jeton d’actualisation (valide 60 jours) pour obtenir une nouvelle paire sans redemander l’accord de l’utilisateur :
curl -X POST https://api.woupi.buzz/oauth2/token \
-d grant_type=refresh_token \
-d client_id=YOUR_CLIENT_ID \
-d client_secret=wps_YOUR_CLIENT_SECRET \
-d refresh_token=wpr_Hk2P9sLx4Tq7Wm1Zc8Vb5Nr0Yd3Gf6Ja2Ue9Io4Kp7{
"access_token": "wpa_9Rt2Yw6Ue0Io4Pa8Sd3Fg7Hj1Kl5Zx9Cv2Bn6Mq0Wz",
"refresh_token": "wpr_Nf3Gh7Jk1Lz5Xc9Vb2Nm6Qw0Er4Ty8Ui3Op7As1Dp",
"expires_in": 14400,
"scope": "user:read:email chat:read chat:write",
"token_type": "bearer"
}- Les jetons d’actualisation sont remplacés à chaque utilisation : chaque actualisation renvoie un nouveau jeton d’actualisation et révoque immédiatement l’ancien, ainsi que le jeton d’accès émis avec lui. Conservez toujours la nouvelle paire.
- Réutiliser un ancien jeton d’actualisation renvoie
invalid_grant. - Les nouveaux jetons conservent exactement les mêmes portées. Pour en ajouter, faites repasser l’utilisateur par le flux d’autorisation.
- Si l’utilisateur a déconnecté votre application ou si votre secret client a été renouvelé, l’actualisation échoue avec
invalid_grant: demandez à l’utilisateur d’autoriser de nouveau l’application.
Attention
Si plusieurs processus partagent un jeton, actualisez-le depuis un seul endroit (ou sous verrou). Deux actualisations simultanées avec le même jeton d’actualisation laissent l’un des processus avec une paire révoquée.
Valider les jetons
Appelez le point de terminaison de validation pour vérifier qu’un jeton est toujours valide et savoir à qui il appartient. L’en-tête accepte le préfixe OAuth ou Bearer; aucun Client-Id n’est requis.
curl https://api.woupi.buzz/oauth2/validate \
-H "Authorization: OAuth wpa_3pXq9vN0cT7yLw2KzR5mB8dF1hJ4sG6aE0uI2oY7tQ"{
"client_id": "k3v9x0q2m8w1r5t7y4u6i0o2p8a1s3",
"login": "streamerdemo",
"user_id": "42",
"scopes": ["user:read:email", "chat:read", "chat:write"],
"expires_in": 13877
}expires_in indique le nombre de secondes restantes. Pour un jeton d’application, login et user_id valent null. Un jeton invalide, expiré ou révoqué renvoie :
HTTP/1.1 401 Unauthorized
Content-Type: application/json
{"status":401,"message":"invalid access token"}Validez les jetons d’utilisateur au démarrage de votre application et chaque fois qu’un appel à l’API renvoie 401, afin de détecter rapidement qu’un utilisateur a déconnecté votre application.
Révoquer les jetons
Révoquez un jeton lorsque l’utilisateur se déconnecte de votre application ou que vous n’en avez plus besoin. Vous pouvez envoyer un jeton d’accès ou un jeton d’actualisation; la révocation de l’un révoque aussi l’autre jeton de la même paire.
curl -X POST https://api.woupi.buzz/oauth2/revoke \
-d client_id=YOUR_CLIENT_ID \
-d token=wpa_3pXq9vN0cT7yLw2KzR5mB8dF1hJ4sG6aE0uI2oY7tQLe point de terminaison répond 200, que le jeton ait existé ou non : vous pouvez donc l’appeler plusieurs fois sans risque.
Authentification du client
Au point de terminaison de jetons, les applications confidentielles s’authentifient avec leur identifiant et leur secret client, soit dans le corps de la requête (client_id et client_secret, comme dans les exemples ci-dessus), soit par authentification HTTP Basic :
curl -X POST https://api.woupi.buzz/oauth2/token \
-u YOUR_CLIENT_ID:wps_YOUR_CLIENT_SECRET \
-d grant_type=client_credentialsLes applications publiques n’envoient que client_id dans le corps. Un secret erroné ou absent pour une application confidentielle renvoie 401 invalid_client.
Erreurs OAuth
Les points de terminaison OAuth utilisent le format d’erreur standard, avec le statut HTTP 400 (ou 401 en cas d’échec de l’authentification du client) :
{
"error": "invalid_grant",
"error_description": "The authorization code is invalid, expired or already used"
}error | Signification |
|---|---|
invalid_request | Un paramètre obligatoire est absent ou mal formé, ou PKCE manque pour une application publique. |
invalid_client | client_id inconnu, ou secret client erroné ou absent. |
invalid_grant | Le code, le jeton d’actualisation ou le code d’appareil est invalide, expiré, déjà utilisé ou émis pour une autre application, ou encore le redirect_uri ou le code_verifier ne correspond pas. |
unauthorized_client | L’application ne peut pas utiliser cet octroi (par exemple une application publique avec client_credentials) ou le compte de son développeur n’est pas approuvé. |
unsupported_grant_type | grant_type inconnu. |
invalid_scope | Une portée demandée n’existe pas. |
authorization_pending | Flux par code d’appareil : l’utilisateur n’a pas encore approuvé. |
slow_down | Flux par code d’appareil : vous interrogez trop souvent. |
access_denied | L’utilisateur a refusé l’autorisation. |
expired_token | Flux par code d’appareil : le code d’appareil a expiré. |
Les erreurs de l’API REST utilisent une autre enveloppe, décrite dans Limites de débit et erreurs.
Bonnes pratiques de sécurité
- N’intégrez jamais un secret client dans un navigateur, une application mobile ou de bureau, ni dans un client de jeu. Enregistrez ces applications comme publiques et utilisez PKCE.
- Générez un nouveau
statealéatoire pour chaque autorisation, conservez-le dans la session de l’utilisateur et rejetez le retour si lestatereçu est différent. - Échangez les codes et les jetons d’actualisation depuis votre serveur. Conservez les jetons d’accès et d’actualisation côté serveur (chiffrés au repos), jamais dans des URL ni dans des journaux.
- Ne demandez que les portées dont vous avez besoin (voir Portées).
- Enregistrez des URI de redirection exacts en HTTPS; réservez les URI
http://localhostau développement. - Révoquez les jetons lorsqu’un utilisateur se déconnecte et renouvelez immédiatement votre secret client depuis le portail en cas de fuite.
