Authentifizierungsverhalten von API-Endpunkten in ASP.NET Core

Bei Verwendung der cookie Authentifizierung geben API-Endpunkte die entsprechenden HTTP-Statuscodes (401 oder 403) für Authentifizierungsfehler zurück, anstatt nicht authentifizierte Anforderungen an Anmeldeseiten umzuleiten. Dieses Verhalten, das für den programmgesteuerten API-Zugriff besser geeignet ist, wurde in ASP.NET Core in .NET 10 eingeführt.

Wie ASP.NET Core API-Endpunkte identifiziert

ASP.NET Core fügt IDisableCookieRedirectMetadata zu den Endpunkten hinzu, die es als API-Endpunkte erkennt, darunter:

  • Controller, die mit dem [ApiController] Attribut versehen sind.
  • Minimale API-Endpunkte, die JSON-Anforderungstexte lesen oder JSON-Antworten schreiben.
  • Endpunkte, die TypedResults Rückgabetypen verwenden.
  • SignalR Hubs und Endpunkte.

Die Erkennung basiert auf Metadaten, die abgeleitet werden, wenn die App ihre Endpunkte erstellt. Sie basiert nicht auf dem Accept Header einer eingehenden Anforderung und basiert nicht darauf, welche Map{Verb} Methode die Route registriert hat.

Ein minimaler API-Handler, dessen deklarierter Rückgabetyp void, IResult oder die TypedResults-Schnittstelle ist, trägt über seinen Rückgabetyp nicht zu den Metadaten bei, obwohl die konkreten Ok<TValue>-Typen wie string dies tun. Beispielsweise schreibt app.MapGet("/hello", () => "Hello").RequireAuthorization() eine text/plain-Antwort und akzeptiert keinen JSON-Request-Body, sodass nicht authentifizierte Anforderungen an app.MapGet("/hello", () => "Hello").RequireAuthorization() weiterhin auf die Anmeldeseite umgeleitet werden.

Standardverhalten

Standardmäßig wendet cookie ASP.NET Core die Authentifizierungslogik basierend auf dem Endpunkttyp an:

  • Webseiten: Umleiten sie auf die Anmelde- oder Zugriffsverweigerungsseite mit einem Statuscode 302.
  • API-Endpunkte: Zurückgeben von Statuscodes 401 oder 403 anstelle einer 302-Umleitung.

XMLHttpRequests (XHRs) empfangen 401- und 403-Antworten unabhängig vom Endpunkt, auf den sie abzielen. Dieses Verhalten existierte schon vor .NET 10 und ist unverändert.

Note

Endpunktmetadaten betreffen nur die Herausforderung (401) und verbieten (403) Pfade. Abmelde-Umleitungen sind nicht betroffen. Wenn eine Abmeldeanforderung einen Umleitungs-URI oder eine gültige Rückgabe-URL angibt, werden Nicht-XHR-Anforderungen wie vor .NET 10 umgeleitet. Das XHR-Verhalten ist unverändert.

Konfigurieren des Verhaltens für bestimmte Endpunkte

Aufrufen DisableCookieRedirect , um 401- und 403-Statuscodes für Endpunkte zurückzugeben, die nicht automatisch erkannt werden:

var api = app.MapGroup("/api").DisableCookieRedirect();

api.MapGet("/status", () => "Ready")
   .RequireAuthorization();

Aufrufen AllowCookieRedirect , um Anmeldeumleitungen für Endpunkte beizubehalten, die als API-Endpunkte erkannt werden:

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

Wenden Sie bei Controllern AllowCookieRedirectAttribute auf eine Aktionsmethode oder auf die Controllerklasse an:

[ApiController]
[Authorize]
[AllowCookieRedirect]
[Route("[controller]")]
public class ReportsController : ControllerBase
{
    [HttpGet("summary")]
    public object GetSummary() => new { Total = 1000 };
}

Important

IAllowCookieRedirectMetadata überschreibt IDisableCookieRedirectMetadata unabhängig von der Reihenfolge, in der die Metadaten hinzugefügt werden. Der Aufruf von DisableCookieRedirect nach AllowCookieRedirect auf demselben Endpunkt stellt das Verhalten des Statuscodes nicht wieder her.

Verhalten in der gesamten App deaktivieren

Um das Vor-.NET 10-Verhalten für eine gesamte App wiederherzustellen, aktivieren Sie den Microsoft.AspNetCore.Authentication.Cookies.IgnoreRedirectMetadata Schalter. Wenn sie aktiviert ist, cookie ignoriert die Authentifizierung Endpunktmetadaten, und nur XHRs führen zu 401- und 403-Antworten.

Legen Sie den Switch in der Projektdatei so fest, dass er angewendet wird, bevor app-Code ausgeführt wird:

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

Der Schalter kann auch im Code festgelegt werden. Es wird nur einmal gelesen, nämlich beim ersten Verwenden der Authentifizierung cookie. Rufen Sie daher SetSwitch auf, bevor der Host erstellt wird:

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

var builder = WebApplication.CreateBuilder(args);

Wichtige Überlegungen zu Änderungen

Die in .NET 10 eingeführte Änderung ist eine Verhaltensänderung. Eine App, die cookie-Authentifizierung mit Endpunkten kombiniert, die ASP.NET Core als API-Endpunkte erkennt, gibt 401- und 403-Antworten zurück, wo zuvor eine 302-Umleitung zur Anmelde- oder Seite „Zugriff verweigert“ zurückgegeben wurde. Die vollständige änderungsbemerkung finden Sie unter Cookie "Anmeldeumleitungen" für bekannte API-Endpunkte deaktiviert.

Berücksichtigen Sie die Auswirkungen auf jede Art von App:

  • Webanwendungen: Seitenendpunkte werden weiterhin auf die Anmeldeseite umgeleitet.
  • Gemischte Anwendungen: API-Endpunkte geben Statuscodes zurück, während Webseiten Umleitungen erhalten, sodass Browsercode, der auf die Umleitung folgt, stattdessen 401 und 403 verarbeiten muss.
  • Nur API-Anwendungen: Erkannte API-Endpunkte geben die richtigen HTTP-Statuscodes ohne zusätzliche Konfiguration zurück.

Um das vorherige Verhalten beizubehalten, rufen Sie AllowCookieRedirect für die betroffenen Endpunkte auf, wenden Sie [AllowCookieRedirect] auf die betroffenen Controller an oder aktivieren Sie den Schalter Microsoft.AspNetCore.Authentication.Cookies.IgnoreRedirectMetadata für die gesamte App.

Testen ihrer API-Endpunkte

Überprüfen Sie nach dem Upgrade auf ASP.NET Core in .NET 10, dass Ihre API-Endpunkte geeignete Statuscodes zurückgeben:

[Fact]
public async Task UnauthorizedApiRequest_Returns401()
{
    var response = await client.GetAsync("/api/secure-data");

    Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);

    // The handler sets the Location header before setting the status code,
    // so the login URI is still present on the 401 response.
    Assert.NotNull(response.Headers.Location);
}