1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

C#でOpenAPIを使ってBearer認証をする

Last updated at Posted at 2023-04-20

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. 補足

  • 秘密鍵は何でもよいのだが自由があるだけに設定に悩み、下記サイトを参考にさせてもらった

image.png

7. 参考文献

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?