Cookie 既知の API エンドポイントに対してログイン リダイレクトが無効になっている

既定では、 cookie 認証によって保護された既知の API エンドポイントに対して行われた認証されていない要求と未承認の要求により、ログインまたはアクセス拒否 URI にリダイレクトされるのではなく、401 と 403 の応答が返されるようになりました。

既知 の API エンドポイント は、新しい IDisableCookieRedirectMetadata インターフェイスを使用して識別され、新しいインターフェイスを実装するメタデータが自動的に次に追加されています。

  • [ApiController] エンドポイント。
  • JSON 要求本文を読み取ったり、JSON 応答を書き込んだりする最小限の API エンドポイント。
  • TypedResults型の戻り値を使用するエンドポイント。
  • SignalR エンドポイント。

コンパニオン IAllowCookieRedirectMetadata インターフェイスでは、エンドポイントをリダイレクトの対象に戻します。 メタデータが追加される順序に関係なく、 IDisableCookieRedirectMetadata がオーバーライドされます。

401 と 403 の応答には、ログインまたはアクセス拒否 URI を保持する Location ヘッダーが引き続き含まれます。 状態コードのみが変更されます。

エンドポイント メタデータを考慮するのは、チャレンジ (401) パスと禁止パス (403) のみです。 サインアウトリダイレクトとリターン URL リダイレクトは影響を受けません。

導入されたバージョン

.NET 10 Preview 7

以前の動作

以前は、 cookie 認証ハンドラーは、 XMLHttpRequests (XR) 以外のすべての要求に対して、認証されていない要求と未承認の要求をログインまたはアクセス拒否 URI に既定でリダイレクトしました。

新しい動作

.NET 10 以降では、既知の API エンドポイントに対して行われた認証されていない要求と未承認の要求により、ログインまたはアクセス拒否 URI にリダイレクトされるのではなく、401 と 403 の応答が返されます。 XR は、ターゲット エンドポイントに関係なく、引き続き 401 応答と 403 応答を返します。

破壊的変更の種類

この変更は 動作の変更です。

変更の理由

この変更は非常に要求されました。 認証されていない要求をログイン ページにリダイレクトすることは、通常は API エンドポイントにとって意味がありません。通常は、認証エラーを伝えるために HTML リダイレクトではなく、401 と 403 の状態コードに依存します。

アプリ全体の以前の動作を復元するには、 Microsoft.AspNetCore.Authentication.Cookies.IgnoreRedirectMetadata スイッチを有効にします。 Cookie認証ではエンドポイント メタデータが無視され、XHR のみが 401 および 403 応答になり、.NET 10 より前の動作と一致します。 アプリ コードが実行される前に適用されるように、プロジェクト ファイルでスイッチを設定します。

<ItemGroup>
  <RuntimeHostConfigurationOption
    Include="Microsoft.AspNetCore.Authentication.Cookies.IgnoreRedirectMetadata"
    Value="true" />
</ItemGroup>

このスイッチが読み取られるのは、cookie 認証が最初に使用されるときの 1 回だけです。そのため、代わりにコードで設定する場合は、ホストがビルドされる前に SetSwitch を呼び出してください。

AppContext.SetSwitch(
    "Microsoft.AspNetCore.Authentication.Cookies.IgnoreRedirectMetadata", true);

var builder = WebApplication.CreateBuilder(args);

代わりに個々のエンドポイントのリダイレクトを復元するには、 AllowCookieRedirect を呼び出すか、アクション メソッドまたはコントローラー クラスに AllowCookieRedirectAttribute を適用します。

app.MapGet("/reports/summary", () => new { Total = 1000 })
   .RequireAuthorization()
   .AllowCookieRedirect();

逆に、 DisableCookieRedirect を呼び出して、API エンドポイントとして自動的に検出されないエンドポイントの状態コード 401 と 403 を返します。

ターゲット エンドポイントに関係なく、または要求のソースが XHR であるかどうかに関係なく、認証されていない要求または承認されていない要求のログインとアクセス拒否 URI に常にリダイレクトする場合は、次のように RedirectToLogin と RedirectToAccessDenied をオーバーライドできます。

builder.Services.AddAuthentication()
    .AddCookie(options =>
    {
        options.Events.OnRedirectToLogin = context =>
        {
            context.Response.Redirect(context.RedirectUri);
            return Task.CompletedTask;
        };

        options.Events.OnRedirectToAccessDenied = context =>
        {
            context.Response.Redirect(context.RedirectUri);
            return Task.CompletedTask;
        };
    });

XR のみのリダイレクトを回避する以前の正確な動作に戻す場合は、この少し複雑なロジックでイベントをオーバーライドできます。

builder.Services.AddAuthentication()
    .AddCookie(options =>
    {
        bool IsXhr(HttpRequest request)
        {
            return string.Equals(request.Query[HeaderNames.XRequestedWith], "XMLHttpRequest", StringComparison.Ordinal) ||
                string.Equals(request.Headers.XRequestedWith, "XMLHttpRequest", StringComparison.Ordinal);
        }

        options.Events.OnRedirectToLogin = context =>
        {
            if (IsXhr(context.Request))
            {
                context.Response.Headers.Location = context.RedirectUri;
                context.Response.StatusCode = 401;
            }
            else
            {
                context.Response.Redirect(context.RedirectUri);
            }

            return Task.CompletedTask;
        };

        options.Events.OnRedirectToAccessDenied = context =>
        {
            if (IsXhr(context.Request))
            {
                context.Response.Headers.Location = context.RedirectUri;
                context.Response.StatusCode = 403;
            }
            else
            {
                context.Response.Redirect(context.RedirectUri);
            }

            return Task.CompletedTask;
        };
    });

影響を受ける API

こちらも参照ください