$npx -y skills add wshaddix/dotnet-skills --skill dotnet-api-securityImplementing API auth. Identity, OAuth/OIDC, JWT bearer, passkeys (WebAuthn), CORS, rate limiting.
| 1 | # dotnet-api-security |
| 2 | |
| 3 | API-level authentication, authorization, and security patterns for ASP.NET Core. This skill owns API auth implementation: ASP.NET Core Identity configuration, OAuth 2.0/OIDC integration, JWT bearer token handling, passkey (WebAuthn) authentication, CORS policies, Content Security Policy headers, and rate limiting. |
| 4 | |
| 5 | **Auth ownership:** This skill owns API-level auth patterns. Blazor-specific auth UI (AuthorizeView, CascadingAuthenticationState, client-side token handling) -- see [skill:dotnet-blazor-auth] when it lands. OWASP security principles (cross-cutting vulnerability mitigations) -- see [skill:dotnet-security-owasp]. |
| 6 | |
| 7 | **Out of scope:** OWASP Top 10 mitigations and deprecated security patterns -- see [skill:dotnet-security-owasp]. Secrets management and secure configuration -- see [skill:dotnet-secrets-management]. Cryptographic algorithm selection -- see [skill:dotnet-cryptography]. Blazor auth UI components -- see [skill:dotnet-blazor-auth]. |
| 8 | |
| 9 | Cross-references: [skill:dotnet-security-owasp] for OWASP security principles, [skill:dotnet-secrets-management] for secrets handling, [skill:dotnet-cryptography] for cryptographic best practices. |
| 10 | |
| 11 | --- |
| 12 | |
| 13 | ## ASP.NET Core Identity |
| 14 | |
| 15 | ASP.NET Core Identity provides user management, password hashing, role-based authorization, and two-factor authentication out of the box. It is the recommended starting point for applications that manage their own user accounts. |
| 16 | |
| 17 | ```csharp |
| 18 | builder.Services.AddIdentityApiEndpoints<ApplicationUser>(options => |
| 19 | { |
| 20 | // Password requirements |
| 21 | options.Password.RequiredLength = 12; |
| 22 | options.Password.RequireNonAlphanumeric = true; |
| 23 | options.Password.RequireUppercase = true; |
| 24 | options.Password.RequireLowercase = true; |
| 25 | options.Password.RequireDigit = true; |
| 26 | |
| 27 | // Lockout |
| 28 | options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(15); |
| 29 | options.Lockout.MaxFailedAccessAttempts = 5; |
| 30 | options.Lockout.AllowedForNewUsers = true; |
| 31 | |
| 32 | // User |
| 33 | options.User.RequireUniqueEmail = true; |
| 34 | }) |
| 35 | .AddEntityFrameworkStores<AppDbContext>() |
| 36 | .AddDefaultTokenProviders(); |
| 37 | |
| 38 | var app = builder.Build(); |
| 39 | |
| 40 | app.MapIdentityApi<ApplicationUser>(); // Maps /register, /login, /refresh, /manage endpoints |
| 41 | ``` |
| 42 | |
| 43 | ### Identity API Endpoints (.NET 8+) |
| 44 | |
| 45 | `MapIdentityApi<TUser>()` provides pre-built token-based authentication endpoints for SPAs and mobile clients without Razor UI: |
| 46 | |
| 47 | | Endpoint | Method | Description | |
| 48 | |----------|--------|-------------| |
| 49 | | `/register` | POST | Create a new user account | |
| 50 | | `/login` | POST | Authenticate and receive tokens | |
| 51 | | `/refresh` | POST | Refresh an expired access token | |
| 52 | | `/confirmEmail` | GET | Confirm email address | |
| 53 | | `/manage/info` | GET/POST | Get/update user profile | |
| 54 | | `/manage/2fa` | POST | Configure two-factor authentication | |
| 55 | |
| 56 | --- |
| 57 | |
| 58 | ## OAuth 2.0 / OpenID Connect |
| 59 | |
| 60 | For applications that delegate authentication to an external identity provider (Entra ID, Auth0, Okta, Keycloak), configure OIDC middleware. |
| 61 | |
| 62 | ```csharp |
| 63 | builder.Services.AddAuthentication(options => |
| 64 | { |
| 65 | options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; |
| 66 | options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; |
| 67 | }) |
| 68 | .AddCookie() |
| 69 | .AddOpenIdConnect(options => |
| 70 | { |
| 71 | options.Authority = builder.Configuration["Oidc:Authority"]; |
| 72 | options.ClientId = builder.Configuration["Oidc:ClientId"]; |
| 73 | options.ClientSecret = builder.Configuration["Oidc:ClientSecret"]; |
| 74 | options.ResponseType = OpenIdConnectResponseType.Code; // Authorization Code Flow |
| 75 | options.SaveTokens = true; |
| 76 | options.GetClaimsFromUserInfoEndpoint = true; |
| 77 | |
| 78 | options.Scope.Add("openid"); |
| 79 | options.Scope.Add("profile"); |
| 80 | options.Scope.Add("email"); |
| 81 | |
| 82 | options.MapInboundClaims = false; // Preserve original claim types |
| 83 | options.TokenValidationParameters.NameClaimType = "name"; |
| 84 | options.TokenValidationParameters.RoleClaimType = "roles"; |
| 85 | }); |
| 86 | ``` |
| 87 | |
| 88 | **Gotcha:** `MapInboundClaims = false` prevents the Microsoft OIDC handler from remapping standard JWT claims (e.g., `sub` to `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier`). Set this to `false` to preserve the original claim types from the identity provider. |
| 89 | |
| 90 | --- |
| 91 | |
| 92 | ## JWT Bearer Token Authentication |
| 93 | |
| 94 | For API-only scenarios where the client sends a JWT in the `Authorization` header: |
| 95 | |
| 96 | ```csharp |
| 97 | builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) |
| 98 | .AddJwtBearer(options => |
| 99 | { |
| 100 | options.Authority = builder.Configuration["Jwt:Authority"]; |
| 101 | options.Audience = builder.Configuration["Jwt:Audience"]; |
| 102 | |
| 103 | options.TokenValidationParameters = new TokenValidationParameters |
| 104 | { |
| 105 | ValidateIssuer = true, |
| 106 | ValidateAudience = true, |
| 107 | ValidateLifetime = true, |
| 108 | ValidateIssuerSigningKey = true, |
| 109 | ClockSkew = TimeSpan.From |