PluginBench
Skill
Pass
Audit score 90

authentication

codewithmukesh/dotnet-claude-kit

JWT, OpenID Connect, and policy-based authorization for ASP.NET Core APIs and web apps.

What is authentication?

Implements authentication and authorization in ASP.NET Core using JWT bearer tokens, cookies, OpenID Connect, and ASP.NET Identity. Use this when building login flows, protecting endpoints, designing authorization rules, or integrating external identity providers.

  • Configure JWT bearer token validation with issuer, audience, and signing key checks
  • Generate and validate JWT tokens with claims and role information
  • Define policy-based authorization rules composable with custom requirements and handlers
  • Protect API endpoints and endpoint groups with authorization policies
  • Integrate external identity providers via OpenID Connect
  • Access current user claims and identity in handlers via ClaimsPrincipal

How to install authentication

npx skills add https://github.com/codewithmukesh/dotnet-claude-kit --skill authentication
Prerequisites
  • ASP.NET Core 6.0 or later
  • Microsoft.AspNetCore.Authentication.JwtBearer NuGet package for JWT support
  • Microsoft.AspNetCore.Identity for user management (optional but recommended)
  • Configuration values for JWT issuer, audience, and signing key
Claude Code
Cursor
Windsurf
Cline

How to use authentication

  1. 1.Add authentication services to Program.cs with JWT bearer configuration and token validation parameters
  2. 2.Configure authorization policies using AddAuthorizationBuilder() with roles, claims, or custom requirements
  3. 3.Implement a TokenService to generate JWT tokens with user claims and expiration
  4. 4.Protect endpoint groups or individual routes with RequireAuthorization() and policy names
  5. 5.Access the current user via ClaimsPrincipal parameter injection in handlers
  6. 6.Store secrets in user secrets (development) or Azure Key Vault (production), never in appsettings.json

Use cases

Good for
  • Securing REST APIs with JWT bearer authentication and token generation
  • Implementing role and claim-based authorization policies for different user groups
  • Protecting Minimal API endpoints with policy-based authorization decorators
  • Integrating single sign-on via OpenID Connect with external providers
  • Building passwordless login with ASP.NET Identity passkeys (WebAuthn)
Who it's for
  • Backend developers building ASP.NET Core APIs
  • Full-stack developers implementing authentication in Blazor or MVC applications
  • Security-focused engineers designing authorization policies
  • Teams integrating external identity providers or SSO

authentication FAQ

Should I use JWT or cookies for authentication?

Use JWT for REST APIs and stateless services; use cookies for Blazor Server and traditional MVC web apps where you maintain server-side session state.

What is the difference between role-based and policy-based authorization?

Role-based uses magic strings like [Authorize(Roles="Admin")] and is hard to test; policy-based defines named policies with composable requirements, is testable, and more maintainable.

How do I integrate an external identity provider like Azure AD or Auth0?

Use OpenID Connect by configuring AddOpenIdConnect() with the provider's Authority, ClientId, and ClientSecret, then add cookie authentication as the default scheme.

Where should I store JWT secrets and configuration?

Use dotnet user-secrets in development and Azure Key Vault or environment variables in production; never commit secrets to appsettings.json.

How do I add passwordless login to my application?

Use ASP.NET Identity with built-in passkey/WebAuthn support (available since .NET 10) instead of password-based authentication.

Full instructions (SKILL.md)

Source of truth, from codewithmukesh/dotnet-claude-kit.


name: authentication description: > Authentication and authorization for ASP.NET Core. Covers JWT bearer tokens, OpenID Connect, ASP.NET Identity, authorization policies, role and claim-based authorization, and API key authentication. Load this skill when implementing login, protecting endpoints, designing authorization rules, or when the user mentions "auth", "JWT", "bearer token", "OIDC", "OpenID Connect", "Identity", "claims", "roles", "authorize", "RequireAuthorization", "API key", or "cookie auth".

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, email confirmation, and (since .NET 10) built-in passkey/WebAuthn support for passwordless login.
  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

Use JsonWebTokenHandler from Microsoft.IdentityModel.JsonWebTokens — it is the maintained, span-based handler that ASP.NET Core itself validates with. JwtSecurityTokenHandler (System.IdentityModel.Tokens.Jwt) is the legacy stack.

public sealed class TokenService(IConfiguration config, TimeProvider clock)
{
    private static readonly JsonWebTokenHandler TokenHandler = new();

    public string GenerateToken(User user, IEnumerable<string> roles)
    {
        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config["Jwt:Key"]!));
        var now = clock.GetUtcNow();

        var descriptor = new SecurityTokenDescriptor
        {
            Issuer = config["Jwt:Issuer"],
            Audience = config["Jwt:Audience"],
            IssuedAt = now.UtcDateTime,
            Expires = now.AddHours(1).UtcDateTime,
            Claims = new Dictionary<string, object>
            {
                [JwtRegisteredClaimNames.Sub] = user.Id,
                [JwtRegisteredClaimNames.Email] = user.Email!,
                [JwtRegisteredClaimNames.Name] = user.UserName!,
                ["roles"] = roles.ToArray()
            },
            SigningCredentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256)
        };

        return TokenHandler.CreateToken(descriptor);
    }
}

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

ScenarioRecommendation
REST APIJWT Bearer authentication
Blazor Server / MVCCookie authentication
External identity providerOpenID Connect
User registration / loginASP.NET Identity
Passwordless loginASP.NET Identity passkeys (WebAuthn, built-in since .NET 10)
Permission checkingPolicy-based authorization
Multi-tenant APIClaims-based with tenant claim
API-to-API communicationClient credentials (OAuth 2.0)
Simple API keysCustom AuthenticationHandler<T>