💻 Développement

dev-oauth2-oidc-advisor

Implémentation d'OAuth2, OpenID Connect, JWT et gestion des tokens pour sécuriser des APIs et applications web.

⚡ Installation & lancement en 1 commande

Copiez-collez dans votre terminal : le skill s'installe dans ~/.claude/skills et Claude Code se lance directement dessus.

macOS / Linux
curl -fsSL https://raw.githubusercontent.com/khalilbenaz/claude-skills-collection/main/install.sh | sh -s -- dev-oauth2-oidc-advisor --launch
Windows (PowerShell)
iex "& { $(iwr -useb https://raw.githubusercontent.com/khalilbenaz/claude-skills-collection/main/install.ps1) } dev-oauth2-oidc-advisor -Launch"

🚀 Déjà installé ?

claude "/dev-oauth2-oidc-advisor"

Ou tapez /dev-oauth2-oidc-advisor dans une session Claude Code, ou décrivez simplement votre besoin — le skill se déclenche automatiquement via le skill-router.

🔑 Déclencheurs automatiques

Le skill s'active automatiquement quand votre demande contient :

OAuth2OIDCOpenID ConnectJWTtokenrefresh tokenidentity providerKeycloakAzure ADAuth0

📦 Installation manuelle

git clone https://github.com/khalilbenaz/claude-skills-collection.git cp -r claude-skills-collection/skills/dev-oauth2-oidc-advisor ~/.claude/skills/

Payload du plugin : skills/dev-oauth2-oidc-advisor · source éditable : dev-skills/oauth2-oidc-advisor

📖 Manuel

Conseiller OAuth2 / OIDC

Workflow en 5 étapes

  1. Qualifier le contexte : type de client (SPA, mobile, service, CLI), confidentialité du secret, présence d'un utilisateur humain ou non.
  2. Choisir le flow : voir tableau ci-dessous — la mauvaise sélection est la source d'erreur #1.
  3. Configurer l'IdP : clients, redirect URIs, scopes, claims mapping.
  4. Implémenter côté serveur : middleware de validation JWT, politiques d'autorisation.
  5. Durcir : rotation des secrets, expiration courte, révocation, PKCE obligatoire.

Choisir le bon flow

FlowQuand l'utiliserType client
Authorization Code + PKCEApp web, SPA, mobile — utilisateur présentPublic ou confidentiel
Client CredentialsService-to-service, daemon, batchConfidentiel uniquement
Device CodeIoT, CLI, smart TV — pas de navigateurPublic
Refresh TokenRenouvellement silencieux de l'access tokenTous

Règle décisive : y a-t-il un utilisateur humain qui s'authentifie ?

Flows à bannir définitivement


Implémentation ASP.NET Core

API protégée par JWT Bearer

// Program.cs
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.Authority = "https://auth.company.com";      // Auto-découverte OIDC
        options.Audience  = "payment-api";
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer    = true,
            ValidateAudience  = true,
            ValidateLifetime  = true,
            ClockSkew         = TimeSpan.FromSeconds(30),    // Tolérance horaire minimale
            ValidIssuers      = ["https://auth.company.com"] // Whitelist explicite
        };
        // Événement de débogage pratique
        options.Events = new JwtBearerEvents
        {
            OnAuthenticationFailed = ctx =>
            {
                ctx.Response.Headers.Append("WWW-Authenticate-Detail", ctx.Exception.Message);
                return Task.CompletedTask;
            }
        };
    });

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("PaymentAdmin", p => p.RequireClaim("role", "payment-admin"));
    options.AddPolicy("ReadPayments",  p => p.RequireClaim("scope", "payments:read"));
});

Client Credentials — appel service-to-service

// Avec Duende.AccessTokenManagement ou Microsoft.Extensions.Http.OAuth2
builder.Services.AddClientCredentialsTokenManagement()
    .AddClient("payment-api", client =>
    {
        client.TokenEndpoint = "https://auth.company.com/connect/token";
        client.ClientId      = "order-service";
        client.ClientSecret  = config["Auth:ClientSecret"]; // Depuis Key Vault, pas appsettings
        client.Scope         = "payments:write";
    });

builder.Services.AddHttpClient<IPaymentClient, PaymentClient>()
    .AddClientCredentialsTokenHandler("payment-api");

Extraction de claims dans un controller

[Authorize(Policy = "PaymentAdmin")]
[HttpPost("payments")]
public IActionResult CreatePayment(CreatePaymentRequest request)
{
    var sub      = User.FindFirstValue(ClaimTypes.NameIdentifier); // "sub"
    var roles    = User.FindAll(ClaimTypes.Role).Select(c => c.Value);
    var tenantId = User.FindFirstValue("tenant_id");               // Claim custom
    var scopes   = User.FindFirstValue("scope")?.Split(' ') ?? [];
    // ...
}

Structure d'un JWT — anatomie rapide

eyJhbGciOiJSUzI1NiIsImtpZCI6ImtleS0xMjMifQ   ← Header (Base64url)
.eyJpc3MiOiJodHRwczovL2F1dGguY29tcGFueS5jb20iLCAic3ViIjoidXNlci0xMjMiLCAiYXVkIjoicGF5bWVudC1hcGkiLCAiZXhwIjoxNzAwMDAwMDAwLCAiaWF0IjoxNjk5OTk2NDAwLCAic2NvcGUiOiJwYXltZW50czpyZWFkIiwicm9sZSI6InBheW1lbnQtYWRtaW4iLCJ0ZW5hbnRfaWQiOiJ0ZW5hbnQtYWJjIn0=   ← Payload
.SIGNATURE   ← Signature RS256 (vérifiée avec la clé publique de l'IdP via JWKS)

Claims obligatoires à valider : iss, aud, exp, iat, nbf (si présent). kid permet la rotation de clés sans downtime (JWKS endpoint).


Gestion des tokens

TokenDurée recommandéeStockage côté client
Access Token5–15 minMémoire JS (SPA) ou cookie HttpOnly Secure SameSite=Strict
Refresh Token7–30 joursCookie HttpOnly Secure SameSite=Strict uniquement
ID Token5–15 minMémoire — jamais envoyé à une API tierce

Rotation des refresh tokens (Keycloak / Auth0 / Entra)

Flux : Client → POST /token (grant_type=refresh_token, token=RT1)
               ← { access_token, refresh_token: RT2, expires_in }

RT1 est immédiatement invalidé.
Si RT1 est réutilisé → détection de vol → révocation de toute la famille de tokens.

Activer dans Keycloak : Realm Settings → Tokens → Revoke Refresh Token = ON.


Intégration Identity Providers

Keycloak — déclarer un client confidentiel (CLI)

kcadm.sh create clients -r myrealm \
  -s clientId=order-service \
  -s 'protocol=openid-connect' \
  -s 'publicClient=false' \
  -s 'serviceAccountsEnabled=true' \
  -s 'standardFlowEnabled=false' \
  -s 'directAccessGrantsEnabled=false'

Azure Entra ID — token via Client Credentials

curl -X POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token \
  -d "grant_type=client_credentials" \
  -d "client_id={clientId}" \
  -d "client_secret={secret}" \
  -d "scope=https://api.company.com/.default"

Déboguer un JWT immédiatement

# Décoder sans vérifier la signature (debug uniquement)
echo "{JWT}" | cut -d. -f2 | base64 -d 2>/dev/null | jq .

# Vérifier la signature via JWKS
jwt decode --jwks https://auth.company.com/.well-known/jwks.json {JWT}

Garde-fous / Anti-patterns / Pièges

Pièges courants

PiègeSymptômeCorrection
ClockSkew trop large (> 5 min)Tokens expirés acceptésGarder ≤ 30 secondes
Audience non validéeN'importe quelle API accepte le tokenValidateAudience = true + Audience explicite
Secret partagé entre envsCompromis prod via devSecrets distincts par environnement, rotation trimestrielle
Token dans localStorageExfiltrable via XSSCookie HttpOnly obligatoire
Données sensibles dans le payloadPII exposée (Base64 ≠ chiffrement)Ne mettre que des identifiants opaques dans le JWT
RS256 → HS256 downgradeValidation contournée si secret devinableForcer l'algorithme côté serveur, refuser alg:none
Access token de longue duréeSurface d'attaque élevéeMax 15 min, refresh token pour la durée de session
Pas de révocationToken volé valide jusqu'à expirationImplémenter token introspection ou liste de révocation

Checklist sécurité avant mise en production


Bonnes pratiques 2026