--- lang: ja-jp breaks: true --- # ASP.NET Core Web API に `API Key` による独自認証を追加する 2023-07-04 ## 参考資料 > ASP.NET Core の API の認証・認可をカスタマイズする方法 > https://qiita.com/okazuki/items/f66976c8cd71ea99c385 > SWAGGER UI FOR API KEY AUTHENTICATION FLOW WITH .NET CORE 6 > https://gowthamcbe.com/2022/02/21/swagger-ui-for-api-key-authentication-flow-with-net-core-6/ ## テンプレートよりソリューションを作成 ![](https://hackmd.io/_uploads/rkCAZfZFn.png) ![](https://hackmd.io/_uploads/BydgMzWt2.png) ## CustomAuthenticationHandler.cs ```csharp= public class CustomAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions> { public CustomAuthenticationHandler( IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder ) : base( options, logger, encoder ) { } protected override Task<AuthenticateResult> HandleAuthenticateAsync() { var (ok, name, role) = tryGetApiKey(Context); if (!ok) { return Task.FromResult(AuthenticateResult.Fail("Invalid API Key")); } var p = new ClaimsPrincipal( new ClaimsIdentity( claims: new[] { new Claim(ClaimTypes.Name, name, ClaimValueTypes.String), new Claim(ClaimTypes.Role, role, ClaimValueTypes.String) }, authenticationType: "API KEY AuthType" ) ); return Task.FromResult( AuthenticateResult.Success( new AuthenticationTicket( principal: p, authenticationScheme: "ApiKeyAuthenticationScheme" ) ) ); } (bool ok, string name, string role) tryGetApiKey(HttpContext context) { if (!context.Request.Headers.TryGetValue("API_KEY", out var apiKey)) { return (false, "", ""); } return apiKey.ToString() switch { "A" => (true , "a さん", "Admin"), "B" => (true , "b さん", "User" ), _ => (false, "" , "" ), }; } } ``` ## WeatherForecastController.cs ```csharp= [ApiController] [Route("[controller]")] //[Authorize] // add this line [Authorize(Roles = "Admin")] // add this line public class WeatherForecastController : ControllerBase { private static readonly string[] Summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; private readonly ILogger<WeatherForecastController> _logger; public WeatherForecastController(ILogger<WeatherForecastController> logger) { _logger = logger; } [HttpGet(Name = "GetWeatherForecast")] public IEnumerable<WeatherForecast> Get() { return Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), TemperatureC = Random.Shared.Next(-20, 55), // 名前も返すように変更 Summary = $"{Summaries[Random.Shared.Next(Summaries.Length)]} [{HttpContext?.User?.Identity?.Name}]" }) .ToArray(); } } ``` ## Program.cs ```csharp= var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers(); // デフォルトのスキーマの名前を ApiKeyAuthenticationScheme にして、 // ApiKeyAuthenticationScheme というスキーマで CustomAuthenticationHandler を使うように設定する。 builder.Services .AddAuthentication( defaultScheme: "ApiKeyAuthenticationScheme" ) .AddScheme<AuthenticationSchemeOptions, CustomAuthenticationHandler>( authenticationScheme: "ApiKeyAuthenticationScheme", configureOptions: options => { } ); builder.Services.AddSwaggerGen(c => { // Swaggerの画面に「Authorize」ボタンを追加する。 c.AddSecurityDefinition( name: "ApiKeyDefinition", securityScheme: new OpenApiSecurityScheme { Description = "ApiKey must appear in header", Type = SecuritySchemeType.ApiKey, Name = "API_KEY", In = ParameterLocation.Header, Scheme = "ApiKeyScheme" } ); // リクエストに "API_KEY" ヘッダが追加されるように構成する。 var key = new OpenApiSecurityScheme() { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "ApiKeyDefinition" }, In = ParameterLocation.Header }; var requirement = new OpenApiSecurityRequirement { { key, new List<string>() } }; c.AddSecurityRequirement(requirement); }); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); // 認証認可を有効にする app.UseAuthentication(); // add app.UseAuthorization(); app.MapControllers(); app.Run(); ``` ## 実行結果 ### 認証無し ![](https://hackmd.io/_uploads/ryKMuQWF2.png) ### Aさんで認証 ![](https://hackmd.io/_uploads/B1UEdmZF3.png) ### Bさんで認証 ![](https://hackmd.io/_uploads/r1oH_Q-K3.png)