authentication について
ASP.NET アプリケーション開発者向けの .NET 認証・認可スキルです。Identity、JWT や Cookie による認証、ポリシーベースのアクセス制御を扱います。
主な機能
- 01ASP.NET Identity によるユーザー管理
- 02JWT と Cookie 認証
- 03ポリシーベースの認可
- 04OpenID Connect の設定
- 05シークレットの安全な管理
活用シーン
- 01REST API エンドポイントの保護
- 02Web アプリへのログイン追加
- 03外部 ID プロバイダーとの連携
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ codewithmukesh/ dotnet-claude-kit --skill authenticationインストール後、Claude Code で/authenticationを入力して読み込まれたか確認します。
詳細
認証と認可
基本原則
- ユーザー管理には ASP.NET Identity を使用してください — 独自のユーザーストアを構築しないでください。Identity は、パスワードのハッシュ化、ロックアウト、二要素認証、メール確認に加え、(.NET 10 以降では)パスワードレスログインに対応する組み込みのパスキー/WebAuthn サポートを処理します。
- API には JWT、Web アプリには Cookie を使用してください — API では Bearer トークン認証を使用し、Blazor/MVC アプリでは Cookie 認証を使用します。
- ロールよりポリシーベースの認可を優先してください — ポリシーはテスト可能で、組み合わせ可能であり、
[Authorize(Roles = "Admin")]よりも柔軟に記述できます。 - シークレットをコードに保存しないでください — 開発環境ではユーザーシークレットを使用し、本番環境では Azure Key Vault/環境変数を使用します。
パターン
JWT Bearer 認証
// 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();
トークンの生成
Microsoft.IdentityModel.JsonWebTokens の JsonWebTokenHandler を使用してください。これは保守されている span ベースのハンドラーであり、ASP.NET Core 自体もこれを使って検証します。JwtSecurityTokenHandler(System.IdentityModel.Tokens.Jwt)はレガシーのスタックです。
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);
}
}
ポリシーベースの認可
// 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;
}
}
エンドポイントの保護
// 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(外部 ID プロバイダー)
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");
});
現在のユーザーへのアクセス
// 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();
アンチパターン
あらゆる場所でロール文字列を使用しない
// 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");
シークレットを 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"
トークンの検証を省略しない
// 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)
判断ガイド
| シナリオ | 推奨事項 |
|---|---|
| REST API | JWT Bearer 認証 |
| Blazor Server/MVC | Cookie 認証 |
| 外部 ID プロバイダー | OpenID Connect |
| ユーザー登録/ログイン | ASP.NET Identity |
| パスワードレスログイン | ASP.NET Identity のパスキー(WebAuthn、.NET 10 以降に組み込み) |
| 権限の確認 | ポリシーベースの認可 |
| マルチテナント API | テナントクレームを使用したクレームベース認証 |
| API 間の通信 | クライアントクレデンシャル(OAuth 2.0) |
| シンプルな API キー | カスタム AuthenticationHandler<T> |
コメント
Markdown 対応