
Authentication
- 131 installs
- 629 repo stars
- Updated August 3, 2026
- codewithmukesh/dotnet-claude-kit
Adds ASP.NET Core auth: JWT bearer, OpenID Connect, ASP.NET Identity, API keys, and policy-based role and claim authorization.
About
Implements authentication and authorization for ASP.NET Core, covering JWT bearer tokens, OpenID Connect, ASP.NET Identity, API keys, and policy-based role/claim authorization. A developer uses it to add login, protect endpoints, and design authorization rules.
- Covers JWT bearer, OpenID Connect, ASP.NET Identity, and API key authentication handlers
- Uses policy-based authorization with custom requirements over role strings
Authentication by the numbers
- 131 all-time installs (skills.sh)
- Ranked #933 of 2,203 Security skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/codewithmukesh/dotnet-claude-kit --skill authenticationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 131 |
|---|---|
| repo stars | ★ 629 |
| Last updated | August 3, 2026 |
| Repository | codewithmukesh/dotnet-claude-kit ↗ |
What it does
Adds ASP.NET Core auth: JWT bearer, OpenID Connect, ASP.NET Identity, API keys, and policy-based role and claim authorization.
Files
Authentication & Authorization
Core Principles
1. Use ASP.NET Identity for user management — Don't build your own user store. Identity handles password hashing, lockout, two-factor, and email confirmation. 2. JWT for APIs, cookies for web apps — APIs use Bearer token authentication; Blazor/MVC apps use cookie authentication. 3. Policy-based authorization over roles — Policies are testable, composable, and more expressive than [Authorize(Roles = "Admin")]. 4. Never store secrets in code — Use user secrets in development, Azure Key Vault / environment variables in production.
Patterns
JWT Bearer Authentication
// Program.cs
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidAudience = builder.Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]!)),
ClockSkew = TimeSpan.Zero
};
});
builder.Services.AddAuthorization();Token Generation
public class TokenService(IConfiguration config, TimeProvider clock)
{
public string GenerateToken(User user, IEnumerable<string> roles)
{
var claims = new List<Claim>
{
new(ClaimTypes.NameIdentifier, user.Id),
new(ClaimTypes.Email, user.Email!),
new(ClaimTypes.Name, user.UserName!)
};
claims.AddRange(roles.Select(role => new Claim(ClaimTypes.Role, role)));
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config["Jwt:Key"]!));
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: config["Jwt:Issuer"],
audience: config["Jwt:Audience"],
claims: claims,
expires: clock.GetUtcNow().AddHours(1).DateTime,
signingCredentials: credentials);
return new JwtSecurityTokenHandler().WriteToken(token);
}
}Policy-Based Authorization
// Define policies
builder.Services.AddAuthorizationBuilder()
.AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"))
.AddPolicy("CanManageOrders", policy => policy
.RequireAuthenticatedUser()
.RequireClaim("permission", "orders:write"))
.AddPolicy("MinimumAge", policy => policy
.AddRequirements(new MinimumAgeRequirement(18)));
// Custom requirement + handler
public class MinimumAgeRequirement(int minimumAge) : IAuthorizationRequirement
{
public int MinimumAge => minimumAge;
}
public class MinimumAgeHandler(TimeProvider clock) : AuthorizationHandler<MinimumAgeRequirement>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
MinimumAgeRequirement requirement)
{
var dateOfBirthClaim = context.User.FindFirst("date_of_birth");
if (dateOfBirthClaim is not null &&
DateOnly.TryParse(dateOfBirthClaim.Value, out var dob) &&
dob.AddYears(requirement.MinimumAge) <= DateOnly.FromDateTime(clock.GetUtcNow().DateTime))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}Protecting Endpoints
// Protect an entire group
app.MapGroup("/api/admin")
.WithTags("Admin")
.RequireAuthorization("AdminOnly")
.MapAdminEndpoints();
// Protect individual endpoints
group.MapPost("/", CreateOrder)
.RequireAuthorization("CanManageOrders");
// Allow anonymous on a protected group
group.MapGet("/public-info", GetPublicInfo)
.AllowAnonymous();OpenID Connect (External Identity Provider)
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
options.Authority = builder.Configuration["Oidc:Authority"];
options.ClientId = builder.Configuration["Oidc:ClientId"];
options.ClientSecret = builder.Configuration["Oidc:ClientSecret"];
options.ResponseType = "code";
options.SaveTokens = true;
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("email");
});Accessing Current User
// In minimal API handlers — inject ClaimsPrincipal or HttpContext
group.MapGet("/me", (ClaimsPrincipal user) =>
{
var userId = user.FindFirstValue(ClaimTypes.NameIdentifier);
var email = user.FindFirstValue(ClaimTypes.Email);
return TypedResults.Ok(new { userId, email });
}).RequireAuthorization();Anti-patterns
Don't Use Role Strings Everywhere
// BAD — magic strings, hard to refactor, not testable
[Authorize(Roles = "Admin,SuperAdmin,Manager")]
public class AdminController { }
// GOOD — policy-based
builder.Services.AddAuthorizationBuilder()
.AddPolicy("AdminAccess", p => p.RequireRole("Admin", "SuperAdmin", "Manager"));
group.MapGet("/", Handler).RequireAuthorization("AdminAccess");Don't Store Secrets in appsettings.json
// BAD — committed to source control
{
"Jwt": {
"Key": "super-secret-key-12345"
}
}# GOOD — use user secrets in development
dotnet user-secrets set "Jwt:Key" "super-secret-key-12345"Don't Skip Token Validation
// BAD — disabling validation
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false, // DON'T
ValidateAudience = false, // DON'T
ValidateLifetime = false, // DEFINITELY DON'T
};
// GOOD — validate everything (see JWT Bearer Authentication pattern above for full setup)Decision Guide
| Scenario | Recommendation |
|---|---|
| REST API | JWT Bearer authentication |
| Blazor Server / MVC | Cookie authentication |
| External identity provider | OpenID Connect |
| User registration / login | ASP.NET Identity |
| Permission checking | Policy-based authorization |
| Multi-tenant API | Claims-based with tenant claim |
| API-to-API communication | Client credentials (OAuth 2.0) |
| Simple API keys | Custom AuthenticationHandler<T> |