web-dev-qa-db-ja.com

UseJwtBearerAuthenticationミドルウェアを使用したカスタム401および403応答モデル

401と403が発生したときにJSON応答モデルで応答したい。例えば:

_HTTP 401
{
  "message": "Authentication failed. The request must include a valid and non-expired bearer token in the Authorization header."
}
_

私はミドルウェアを使用して( この回答 で提案されているように)404をインターセプトし、それはうまく機能しますが、401または403には当てはまりません。ミドルウェアは次のとおりです。

_app.Use(async (context, next) =>
{
    await next();
    if (context.Response.StatusCode == 401)
    {
        context.Response.ContentType = "application/json";
        await context.Response.WriteAsync(JsonConvert.SerializeObject(UnauthorizedModel.Create(), SerializerSettings), Encoding.UTF8);
    }
});
_

app.UseJwtBearerAuthentication(..)Startup.Configure(..)の下に配置すると、完全に無視されているように見え、通常の401が返されます。

app.UseJwtBearerAuthentication(..)のABOVEStartup.Configure(..)に配置すると、次の例外がスローされます。

接続ID「0HKT7SUBPLHEM」:未処理の例外がアプリケーションによってスローされました。 System.InvalidOperationException:ヘッダーは読み取り専用であり、応答はすでに開始されています。 Microsoft.AspNetCore.Server.Kestrel.Internal.Http.FrameHeaders.Microsoft.AspNetCore.Http.IHeaderDictionary.set_Item(String key、StringValues value)at Microsoft.AspNetCore.Http.Internal.DefaultHttpResponse.set_ContentType(String value)atMyProject。 Startup.csのApi.Startup。<b__12_0> d.MoveNext()

11
Dave New

セットは正しい方向に進んでいましたが、イベントモデルを利用してデフォルトのチャレンジロジックをオーバーライドできるため、実際には独自のミドルウェアを作成する必要はありません。

これは、OAuth2エラーコード/説明をプレーンテキストとして含む401応答を返す例です(もちろん、JSONまたは必要なものを返すことができます)。

app.UseJwtBearerAuthentication(new JwtBearerOptions
{
    Authority = "http://localhost:54540/",
    Audience = "http://localhost:54540/",
    RequireHttpsMetadata = false,
    Events = new JwtBearerEvents
    {
        OnChallenge = async context =>
        {
            // Override the response status code.
            context.Response.StatusCode = 401;

            // Emit the WWW-Authenticate header.
            context.Response.Headers.Append(
                HeaderNames.WWWAuthenticate,
                context.Options.Challenge);

            if (!string.IsNullOrEmpty(context.Error))
            {
                await context.Response.WriteAsync(context.Error);
            }

            if (!string.IsNullOrEmpty(context.ErrorDescription))
            {
                await context.Response.WriteAsync(context.ErrorDescription);
            }

            context.HandleResponse();
        }
    }
});

または、ステータスコードページミドルウェアを使用することもできますが、403応答の場合、それを引き起こした承認ポリシーについてのヒントはありません。

app.UseStatusCodePages(async context =>
{
    if (context.HttpContext.Request.Path.StartsWithSegments("/api") &&
       (context.HttpContext.Response.StatusCode == 401 ||
        context.HttpContext.Response.StatusCode == 403))
    {
        await context.HttpContext.Response.WriteAsync("Unauthorized request");
    }
});
10
Kévin Chalet