1. はじめに
- OpenAPIをトークンを使用して認証できるようにしたい
- Bearerトークンを発行するようにしたい
2. 開発環境
- C#
- .NET 6
- OpenAPI 3.0
- Visual Studio 2022
- Windows 11
3. 事前準備
- APIのプロジェクトに下記をNuGetでインストールする
- Microsoft.AspNetCore.Authentication.JwtBearer
- Microsoft.IdentityModel.JsonWebTokens
- System.IdentityModel.Tokens.Jwt
4. OpenAPI Specification(OAS)の定義
-
/token
でBearerトークンを発行するAPIを定義する -
security:
で全体に認証対象にしているが、security: []
でトークンを発行するAのみ除外する
openapi: 3.0.0
info:
title: API API Document
version: 0.0.1
description: API のドキュメントです
paths:
/sample:
get:
operationId: GetSample
responses:
'200':
description: OK
/token:
get:
operationId: GetToken
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
token:
type: string
description: Bearerトークン
required:
- token
security: []
components:
securitySchemes:
Bearer:
type: http
scheme: bearer
bearerFormat: JWT
description: Credentials or access token for API
security:
- Bearer: []
5. ソースコード
5.1. トークン発行コントローラー
[AllowAnonymous]
public override IActionResult GetToken()
{
//token生成
var tokenString = JwtBearerUtil.BuildToken();
var response = new
{
token = tokenString
};
return Ok(response);
}
5.2. トークン発行ユーティリティ
- サンプルなので秘密鍵等を定数に定義したが、セキュリティの観点から外部ファイルにする必要がある
/// <summary>
/// JWT(Bearer)に関するユーティリティ
/// </summary>
public static class JwtBearerUtil
{
// 定数
private const string ISSUER_SIGNING_KEY = "4a502f59-d434-4a92-8b5e-8bd23ee762dd";
private const string VALID_ISSUER = "http://localhost:7188/";
private const string VALID_AUDIENCE = "https://localhost:7188/";
private const int EXPIRES_DAYS = 1; // 有効期限:1日
/// <summary>
/// トークン作成
/// </summary>
/// <returns></returns>
public static string BuildToken()
{
// PAYLOADにログインIDを追加
var claims = new List<Claim>();
claims.Add(new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()));
claims.Add(new Claim("loginId", "XXXXX"));
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(ISSUER_SIGNING_KEY));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: VALID_ISSUER,
audience: VALID_AUDIENCE,
claims: claims,
expires: DateTime.Now.AddDays(EXPIRES_DAYS),
signingCredentials: creds
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
public static string getIssuerSigningKey()
{
return ISSUER_SIGNING_KEY;
}
public static string getValidIssuer() {
return VALID_ISSUER;
}
public static string getValidAudience() {
return VALID_AUDIENCE;
}
public static int getExpiresDaysy() {
return EXPIRES_DAYS;
}
}
5.3. プログラム
Program.cs
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
// JWTを登録する
builder.Services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options => {
options.RequireHttpsMetadata = false;
options.SaveToken = true;
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(JwtBearerUtil.getIssuerSigningKey())),
ValidateIssuer = true,
ValidIssuer = JwtBearerUtil.getValidIssuer(),
ValidateAudience = true,
ValidAudience = JwtBearerUtil.getValidAudience(),
RequireExpirationTime = true,
ValidateLifetime = true,
ClockSkew = TimeSpan.FromDays(JwtBearerUtil.getExpiresDaysy()),
};
});
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options => {
options.AddSecurityDefinition("Bearer", new Microsoft.OpenApi.Models.OpenApiSecurityScheme
{
Name = "Authorization",
Type = Microsoft.OpenApi.Models.SecuritySchemeType.Http,
Scheme = "Bearer",
BearerFormat = "JWT",
In = Microsoft.OpenApi.Models.ParameterLocation.Header,
Description = "JWT Authorization header using the Bearer scheme."
});
options.AddSecurityRequirement(new Microsoft.OpenApi.Models.OpenApiSecurityRequirement {
{
new Microsoft.OpenApi.Models.OpenApiSecurityScheme {
Reference = new Microsoft.OpenApi.Models.OpenApiReference {
Type = Microsoft.OpenApi.Models.ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[] {}
}
});
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication(); // 追加
app.UseAuthorization();
app.MapControllers();
app.Run();
6. 補足
- 秘密鍵は何でもよいのだが自由があるだけに設定に悩み、下記サイトを参考にさせてもらった
7. 参考文献