Claude-skill-registry dotnet-api-auth

Authentication and authorization patterns for ASP.NET Core Web APIs

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/dotnet-api-auth" ~/.claude/skills/majiayu000-claude-skill-registry-dotnet-api-auth && rm -rf "$T"
manifest: skills/data/dotnet-api-auth/SKILL.md
source content

.NET API Authentication Skill

Patterns and implementations for securing ASP.NET Core Web APIs with authentication and authorization.

JWT Bearer Authentication

Configure JWT Bearer authentication in

Program.cs
:

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;

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"]!))
        };
    });

// Add after building the app
app.UseAuthentication();
app.UseAuthorization();

Required

appsettings.json
configuration:

{
  "Jwt": {
    "Key": "your-256-bit-secret-key-here-min-32-chars",
    "Issuer": "https://yourdomain.com",
    "Audience": "https://yourdomain.com"
  }
}

API Key Authentication

Custom middleware for API key validation:

public class ApiKeyMiddleware
{
    private const string ApiKeyHeaderName = "X-API-Key";
    private readonly RequestDelegate _next;

    public ApiKeyMiddleware(RequestDelegate next) => _next = next;

    public async Task InvokeAsync(HttpContext context, IConfiguration config)
    {
        if (!context.Request.Headers.TryGetValue(ApiKeyHeaderName, out var extractedApiKey))
        {
            context.Response.StatusCode = 401;
            await context.Response.WriteAsync("API Key is missing");
            return;
        }

        var apiKey = config["ApiKey"];
        if (!apiKey.Equals(extractedApiKey))
        {
            context.Response.StatusCode = 401;
            await context.Response.WriteAsync("Invalid API Key");
            return;
        }

        await _next(context);
    }
}

// Register in Program.cs
app.UseMiddleware<ApiKeyMiddleware>();

Policy-Based Authorization

Define and register authorization policies:

builder.Services.AddAuthorizationBuilder()
    .AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"))
    .AddPolicy("PremiumUser", policy => policy.RequireClaim("Subscription", "Premium"))
    .AddPolicy("MinimumAge", policy =>
        policy.Requirements.Add(new MinimumAgeRequirement(18)));

// Register custom handlers
builder.Services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();

Apply policies to endpoints:

[Authorize(Policy = "AdminOnly")]
[HttpDelete("{id}")]
public IActionResult Delete(int id) { /* ... */ }

// Or with minimal APIs
app.MapDelete("/admin/users/{id}", () => Results.Ok())
    .RequireAuthorization("AdminOnly");

Claims and Roles

Access claims in controllers:

[Authorize]
[HttpGet("profile")]
public IActionResult GetProfile()
{
    var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
    var email = User.FindFirst(ClaimTypes.Email)?.Value;
    var roles = User.FindAll(ClaimTypes.Role).Select(c => c.Value);

    return Ok(new { userId, email, roles });
}

// Check roles
if (User.IsInRole("Admin"))
{
    // Admin-specific logic
}

Generate tokens with claims:

public string GenerateToken(User user)
{
    var claims = new List<Claim>
    {
        new(ClaimTypes.NameIdentifier, user.Id.ToString()),
        new(ClaimTypes.Email, user.Email),
        new(ClaimTypes.Role, user.Role),
        new("CustomClaim", "CustomValue")
    };

    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: DateTime.UtcNow.AddHours(1),
        signingCredentials: credentials);

    return new JwtSecurityTokenHandler().WriteToken(token);
}

Resource-Based Authorization

Authorize access to specific resources:

public class DocumentAuthorizationHandler :
    AuthorizationHandler<OperationAuthorizationRequirement, Document>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        OperationAuthorizationRequirement requirement,
        Document resource)
    {
        var userId = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;

        if (requirement.Name == Operations.Read.Name && resource.OwnerId == userId)
        {
            context.Succeed(requirement);
        }

        if (requirement.Name == Operations.Update.Name &&
            (resource.OwnerId == userId || context.User.IsInRole("Admin")))
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

Use in a controller:

public class DocumentsController : ControllerBase
{
    private readonly IAuthorizationService _authService;

    [HttpGet("{id}")]
    public async Task<IActionResult> Get(int id)
    {
        var document = await _repository.GetAsync(id);
        var result = await _authService.AuthorizeAsync(User, document, Operations.Read);

        if (!result.Succeeded)
            return Forbid();

        return Ok(document);
    }
}

CORS Configuration

Configure CORS for API access:

builder.Services.AddCors(options =>
{
    // Named policy for specific origins
    options.AddPolicy("AllowSpecificOrigin", policy =>
    {
        policy.WithOrigins("https://example.com", "https://app.example.com")
              .AllowAnyMethod()
              .AllowAnyHeader()
              .AllowCredentials();
    });

    // Development policy (more permissive)
    options.AddPolicy("Development", policy =>
    {
        policy.AllowAnyOrigin()
              .AllowAnyMethod()
              .AllowAnyHeader();
    });
});

// Apply globally
app.UseCors("AllowSpecificOrigin");

// Or per-endpoint
app.MapGet("/api/public", () => "Hello")
    .RequireCors("AllowSpecificOrigin");

Quick Reference

PatternUse CaseKey Components
JWT BearerToken-based auth
AddJwtBearer
,
TokenValidationParameters
API KeySimple service authCustom middleware, header validation
Policy-BasedRole/claim requirements
AddPolicy
,
[Authorize(Policy)]
Resource-BasedPer-resource permissions
IAuthorizationService
, custom handlers
CORSCross-origin requests
AddCors
,
UseCors

Additional Resources

  • See
    examples/jwt-setup-example.cs
    for complete JWT configuration
  • See
    examples/policy-auth-example.cs
    for custom authorization requirements