アクセストークンを URLクエリパラメーターでも渡せるようにする

アクセストークンを Authorize ヘッダーではなく、URL クエリパラメーターで渡したい。

認証では Microsoft.AspNetCore.Authentication.JwtBearer を使っていて、何か方法があるのではと調べてみたら、案外簡単に実現できた。

var builder = WebApplication.CreateBuilder(args);
builder.Services
    .AddAuthentication(options =>
    {
        options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(options =>
    {
        options.SaveToken = true;
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidIssuer = builder.Configuration["Issuer"],
            ValidAudience = builder.Configuration["Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(builder.Configuration["SecurityKey"]!)),
        };
        options.Events = new JwtBearerEvents
        {
            // クエリでもアクセストークンを指定できるようにする
            OnMessageReceived = (context) =>
            {
                StringValues values;
                if (!context.Request.Query.TryGetValue("accessToken", out values))
                {
                    return Task.CompletedTask;
                }

                if (values.Count > 1)
                {
                    context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                    context.Fail("指定できる accessToken は 1 つだけです。");

                    return Task.CompletedTask;
                }

                var token = values.Single();

                if (string.IsNullOrWhiteSpace(token))
                {
                    context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                    context.Fail("アクセストークンが空です。");
                    return Task.CompletedTask;
                }

                context.Token = token;
                return Task.CompletedTask;
            }
        };
    });

URL クエリパラメータでアクセストークンを指定して、Web API が呼び出せるようになった。

var response = await client.GetAsync($"/products?accessToken={accessToken}");

もっと早く挑戦しておけばよかったな。