ASP.NET Core JWT bearer authentication: authentication vs authorization explained
Authentication: who you are.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | // ====================================================================== // Q: Authentication vs authorization. How does JWT bearer authentication work in // ASP.NET Core? // // - Authentication: who you are. The result is a ClaimsPrincipal (User). // - Authorization: what you are allowed to do, using roles, policies and // ownership checks (Q36). // Flow: the client obtains an access token from the Identity Provider (Entra ID, // Keycloak, Auth0). The API validates the signature with the IdP's public keys // (JWKS, from /.well-known/openid-configuration), and also checks iss, aud and // exp. Validation is stateless: no database call is needed per request. // - A JWT is signed, not encrypted. Anyone can read it (base64), so we never put // sensitive data in it. // - Revocation: a JWT cannot easily be "cancelled". That is why access tokens // are short-lived (5-15 minutes) and renewed with refresh tokens using // rotation. // - SPA: no tokens in localStorage, because an XSS attack can steal them. Better // is the BFF pattern (Backend-for-Frontend): the server holds the tokens and // the browser only has an HttpOnly, Secure, SameSite cookie. // - Don't write your own authentication server: use an existing IdP or ASP.NET // Core Identity. // ====================================================================== // JWT bearer authentication setup builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(o => { o.Authority = builder.Configuration["Auth:Authority"]; // e.g. https://login.microsoftonline.com/{tenant}/v2.0 o.Audience = builder.Configuration["Auth:Audience"]; // e.g. api://shop-api o.MapInboundClaims = false; // keeps the original claim names (sub, roles) o.TokenValidationParameters = new() { ValidateIssuer = true, ValidateAudience = true, // a token issued for another API is rejected ValidateLifetime = true, ClockSkew = TimeSpan.FromMinutes(1) // default: 5 minutes }; }); // With Entra ID: builder.Services.AddMicrosoftIdentityWebApiAuthentication(builder.Configuration); app.UseAuthentication(); app.UseAuthorization(); |