Conseiller OAuth2 / OIDC
Workflow
- Identifier le flow : Authorization Code, Client Credentials, Device Code, etc.
- Configurer l'IdP : clients, scopes, claims.
- Implémenter : middleware d'authentification, validation de tokens.
- Sécuriser : rotation des secrets, expiration, révocation.
Choisir le bon flow OAuth2
| Flow |
Usage |
Client |
| Authorization Code + PKCE |
Apps web, SPA, mobile |
Public |
| Client Credentials |
Service-to-service, APIs |
Confidentiel |
| Device Code |
IoT, CLI, TV |
Public |
| Refresh Token |
Renouvellement silencieux |
Tous |
Ne plus utiliser
Implicit Flow → remplacé par Authorization Code + PKCE
Resource Owner Password → anti-pattern de sécurité
Implémentation ASP.NET Core
API protégée par JWT
// Program.cs
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "https://auth.company.com";
options.Audience = "payment-api";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ClockSkew = TimeSpan.FromSeconds(30),
ValidIssuers = new[] { "https://auth.company.com" }
};
});
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("PaymentAdmin", policy =>
policy.RequireClaim("role", "payment-admin"));
options.AddPolicy("ReadPayments", policy =>
policy.RequireClaim("scope", "payments:read"));
});
Client Credentials (service-to-service)
// Appel entre services avec token machine-to-machine
builder.Services.AddHttpClient("PaymentApi", client =>
{
client.BaseAddress = new Uri("https://api.company.com");
})
.AddClientCredentialsTokenHandler(options =>
{
options.Authority = "https://auth.company.com";
options.ClientId = "order-service";
options.ClientSecret = configuration["Auth:ClientSecret"];
options.Scope = "payments:write";
});
Extraction de claims
[Authorize]
[ApiController]
public class PaymentController : ControllerBase
{
[HttpPost]
[Authorize(Policy = "PaymentAdmin")]
public async Task<IActionResult> CreatePayment(CreatePaymentRequest request)
{
var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
var roles = User.FindAll(ClaimTypes.Role).Select(c => c.Value);
var tenantId = User.FindFirst("tenant_id")?.Value;
// ...
}
}
Structure d'un JWT
Header.Payload.Signature
{
"alg": "RS256", // Algorithme de signature
"typ": "JWT",
"kid": "key-id-123" // Key ID pour la rotation
}
.
{
"iss": "https://auth.company.com", // Issuer
"sub": "user-123", // Subject (user ID)
"aud": "payment-api", // Audience
"exp": 1700000000, // Expiration
"iat": 1699996400, // Issued at
"scope": "payments:read payments:write",
"role": "payment-admin",
"tenant_id": "tenant-abc"
}
Gestion des tokens
| Token |
Durée de vie |
Stockage |
| Access Token |
5-15 minutes |
Mémoire (SPA), cookie HttpOnly (web) |
| Refresh Token |
7-30 jours |
Cookie HttpOnly secure, rotation obligatoire |
| ID Token |
5-15 minutes |
Mémoire, jamais envoyé à une API |
Rotation des refresh tokens
// Chaque utilisation d'un refresh token en génère un nouveau
// L'ancien est invalidé → détection de vol si réutilisé
Bonnes pratiques
À faire
- Toujours utiliser HTTPS pour les échanges de tokens
- Valider tous les champs du JWT (issuer, audience, expiration)
- Utiliser des scopes granulaires (
payments:read, payments:write)
- Implémenter la rotation des refresh tokens
- Stocker les tokens côté client dans des cookies HttpOnly Secure SameSite
- Utiliser RS256 (asymétrique) plutôt que HS256 pour les APIs publiques
À éviter
- Stocker des tokens dans le localStorage (vulnérable au XSS)
- Des access tokens de longue durée (> 15 min)
- Mettre des données sensibles dans le payload JWT (pas chiffré)
- Faire confiance au token sans vérifier la signature
- Utiliser le même secret pour tous les environnements
Règles
- Le flow Implicit est obsolète — toujours utiliser Authorization Code + PKCE.
- Les access tokens doivent expirer en moins de 15 minutes.
- Les refresh tokens doivent avoir une rotation activée.
- Valider issuer, audience et signature sur chaque requête.
1---2name: oauth2-oidc-advisor3description: Implémentation d'OAuth2, OpenID Connect, JWT et gestion des tokens pour sécuriser des APIs et applications web. À utiliser quand l'utilisateur configure de l'authentification, des tokens JWT, ou intègre un identity provider. Se déclenche aussi avec "OAuth2", "OIDC", "OpenID Connect", "JWT", "token", "refresh token", "identity provider", "Keycloak", "Azure AD", "Auth0".4---5
6# Conseiller OAuth2 / OIDC
7
8## Workflow
9
101. **Identifier le flow** : Authorization Code, Client Credentials, Device Code, etc.
112. **Configurer l'IdP** : clients, scopes, claims.
123. **Implémenter** : middleware d'authentification, validation de tokens.
134. **Sécuriser** : rotation des secrets, expiration, révocation.
14
15## Choisir le bon flow OAuth2
16
17| Flow | Usage | Client |
18|------|-------|--------|
19| **Authorization Code + PKCE** | Apps web, SPA, mobile | Public |
20| **Client Credentials** | Service-to-service, APIs | Confidentiel |
21| **Device Code** | IoT, CLI, TV | Public |
22| **Refresh Token** | Renouvellement silencieux | Tous |
23
24### Ne plus utiliser
25- ~~Implicit Flow~~ → remplacé par Authorization Code + PKCE
26- ~~Resource Owner Password~~ → anti-pattern de sécurité
27
28## Implémentation ASP.NET Core
29
30### API protégée par JWT
31
32```csharp
33// Program.cs
34builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
35 .AddJwtBearer(options =>
36 {
37 options.Authority = "https://auth.company.com";
38 options.Audience = "payment-api";
39 options.TokenValidationParameters = new TokenValidationParameters
40 {
41 ValidateIssuer = true,
42 ValidateAudience = true,
43 ValidateLifetime = true,
44 ClockSkew = TimeSpan.FromSeconds(30),
45 ValidIssuers = new[] { "https://auth.company.com" }
46 };
47 });
48
49builder.Services.AddAuthorization(options =>
50{
51 options.AddPolicy("PaymentAdmin", policy =>
52 policy.RequireClaim("role", "payment-admin"));
53
54 options.AddPolicy("ReadPayments", policy =>
55 policy.RequireClaim("scope", "payments:read"));
56});
57```
58
59### Client Credentials (service-to-service)
60
61```csharp
62// Appel entre services avec token machine-to-machine
63builder.Services.AddHttpClient("PaymentApi", client =>
64{
65 client.BaseAddress = new Uri("https://api.company.com");
66})
67.AddClientCredentialsTokenHandler(options =>
68{
69 options.Authority = "https://auth.company.com";
70 options.ClientId = "order-service";
71 options.ClientSecret = configuration["Auth:ClientSecret"];
72 options.Scope = "payments:write";
73});
74```
75
76### Extraction de claims
77
78```csharp
79[Authorize]
80[ApiController]
81public class PaymentController : ControllerBase
82{
83 [HttpPost]
84 [Authorize(Policy = "PaymentAdmin")]
85 public async Task<IActionResult> CreatePayment(CreatePaymentRequest request)
86 {
87 var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
88 var roles = User.FindAll(ClaimTypes.Role).Select(c => c.Value);
89 var tenantId = User.FindFirst("tenant_id")?.Value;
90
91 // ...
92 }
93}
94```
95
96## Structure d'un JWT
97
98```
99Header.Payload.Signature
100
101{
102 "alg": "RS256", // Algorithme de signature
103 "typ": "JWT",
104 "kid": "key-id-123" // Key ID pour la rotation
105}
106.
107{
108 "iss": "https://auth.company.com", // Issuer
109 "sub": "user-123", // Subject (user ID)
110 "aud": "payment-api", // Audience
111 "exp": 1700000000, // Expiration
112 "iat": 1699996400, // Issued at
113 "scope": "payments:read payments:write",
114 "role": "payment-admin",
115 "tenant_id": "tenant-abc"
116}
117```
118
119## Gestion des tokens
120
121| Token | Durée de vie | Stockage |
122|-------|-------------|----------|
123| **Access Token** | 5-15 minutes | Mémoire (SPA), cookie HttpOnly (web) |
124| **Refresh Token** | 7-30 jours | Cookie HttpOnly secure, rotation obligatoire |
125| **ID Token** | 5-15 minutes | Mémoire, jamais envoyé à une API |
126
127### Rotation des refresh tokens
128
129```csharp
130// Chaque utilisation d'un refresh token en génère un nouveau
131// L'ancien est invalidé → détection de vol si réutilisé
132```
133
134## Bonnes pratiques
135
136### À faire
137- Toujours utiliser **HTTPS** pour les échanges de tokens
138- Valider **tous** les champs du JWT (issuer, audience, expiration)
139- Utiliser des **scopes granulaires** (`payments:read`, `payments:write`)
140- Implémenter la **rotation des refresh tokens**
141- Stocker les tokens côté client dans des **cookies HttpOnly Secure SameSite**
142- Utiliser **RS256** (asymétrique) plutôt que HS256 pour les APIs publiques
143
144### À éviter
145- Stocker des tokens dans le localStorage (vulnérable au XSS)
146- Des access tokens de longue durée (> 15 min)
147- Mettre des données sensibles dans le payload JWT (pas chiffré)
148- Faire confiance au token sans vérifier la signature
149- Utiliser le même secret pour tous les environnements
150
151## Règles
152- Le flow Implicit est obsolète — toujours utiliser Authorization Code + PKCE.
153- Les access tokens doivent expirer en moins de 15 minutes.
154- Les refresh tokens doivent avoir une rotation activée.
155- Valider issuer, audience et signature sur chaque requête.