Zachowanie uwierzytelniania punktu końcowego API w ASP.NET Core

W przypadku korzystania z cookie uwierzytelniania punkty końcowe interfejsu API zwracają odpowiednie kody stanu HTTP (401 lub 403) w przypadku niepowodzeń uwierzytelniania zamiast przekierowywania nieuwierzytelnionych żądań do stron logowania. To zachowanie, które jest bardziej odpowiednie dla programowego dostępu do interfejsu API, zostało wprowadzone w ASP.NET Core na platformie .NET 10.

Jak ASP.NET Core identyfikuje punkty końcowe interfejsu API

ASP.NET Core dodaje IDisableCookieRedirectMetadata do punktów końcowych, które rozpoznaje jako punkty końcowe interfejsu API, w tym:

  • Kontrolery ozdobione atrybutem [ApiController] .
  • Minimalne punkty końcowe interfejsu API, które odczytują treści żądań JSON lub zapisują odpowiedzi JSON.
  • Punkty końcowe korzystające z TypedResults typów zwracanych.
  • SignalR huby i punkty końcowe.

Wykrywanie jest oparte na metadanych, które są wnioskowane, gdy aplikacja kompiluje swoje punkty końcowe. Nie zależy to od nagłówka Accept żądania przychodzącego ani od tego, która metoda Map{Verb} zarejestrowała trasę.

Program obsługi minimalnego interfejsu API, którego zadeklarowany typ zwracany to TypedResults, Ok<TValue> lub interfejs IResult, nie wnosi metadanych poprzez swój typ zwracany, chociaż robią to konkretne typy string, takie jak void. Na przykład app.MapGet("/hello", () => "Hello").RequireAuthorization() zapisuje odpowiedź i nie przyjmuje treści żądania JSON, dlatego nieuwierzytelnione text/plain żądania do niego nadal przekierowuje do strony logowania.

Działanie domyślne

Domyślnie ASP.NET Core stosuje cookie logikę uwierzytelniania na podstawie typu punktu końcowego:

  • Strony sieci Web: przekieruj do strony logowania lub odmowy dostępu przy użyciu kodu stanu 302.
  • Punkty końcowe interfejsu API: zwraca kody stanu 401 lub 403 zamiast przekierowania 302.

Żądania XMLHttpRequests (XHRs) otrzymują odpowiedzi 401 i 403 niezależnie od docelowego punktu końcowego. To zachowanie poprzedza .NET 10 i jest niezmienione.

Note

Metadane punktu końcowego mają wpływ tylko na wyzwanie (401) i zabraniają ścieżek (403). Przekierowania po wylogowaniu nie mają na to wpływu. Gdy żądanie wylogowania określa URI przekierowania lub prawidłowy zwrotny adres URL, żądania inne niż XHR są przekierowywane tak jak przed platformą .NET 10. Zachowanie XHR jest niezmienione.

Konfigurowanie zachowania dla określonych punktów końcowych

Wywołaj metodę DisableCookieRedirect , aby zwrócić kody stanu 401 i 403 dla punktów końcowych, które nie zostały wykryte automatycznie:

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

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

Wywołaj metodę AllowCookieRedirect , aby zachować przekierowania logowania dla punktów końcowych, które są wykrywane jako punkty końcowe interfejsu API:

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

W przypadku kontrolerów zastosuj element AllowCookieRedirectAttribute do metody akcji lub klasy kontrolera:

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

Ważna

IAllowCookieRedirectMetadata zastępuje IDisableCookieRedirectMetadata niezależnie od kolejności dodawania metadanych. Wywołanie DisableCookieRedirect po AllowCookieRedirect w tym samym punkcie końcowym nie przywraca zachowania kodu stanu.

Wyłącz tę funkcję w całej aplikacji

Aby przywrócić zachowanie przed .NET 10 dla całej aplikacji, włącz Microsoft.AspNetCore.Authentication.Cookies.IgnoreRedirectMetadata przełącznik. Po włączeniu tej opcji mechanizm uwierzytelniania cookie ignoruje metadane punktu końcowego, a tylko żądania XHR skutkują odpowiedziami 401 i 403.

Ustaw przełącznik w pliku projektu, aby był stosowany przed uruchomieniem kodu aplikacji:

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

Przełącznik można również ustawić w kodzie. Jest odczytywany tylko raz, przy pierwszym użyciu uwierzytelniania cookie, więc wywołaj SetSwitch przed skompilowaniem hosta:

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

var builder = WebApplication.CreateBuilder(args);

Uwagi dotyczące zmian niekompatybilnych

Zmiana wprowadzona w .NET 10 jest zmianą behawioralną. Aplikacja, która łączy uwierzytelnianie cookie z punktami końcowymi, które ASP.NET Core wykrywa jako punkty końcowe API, zwraca odpowiedzi 401 i 403 zamiast wcześniejszego przekierowania 302 do strony logowania lub strony odmowy dostępu. Aby uzyskać pełną informację o zmianie powodującej niezgodność, zobacz: Cookie przekierowania do logowania są wyłączone dla znanych punktów końcowych interfejsu API.

Rozważ wpływ na poszczególne rodzaje aplikacji:

  • Aplikacje internetowe: punkty końcowe stron nadal przekierowują do strony logowania.
  • Aplikacje mieszane: punkty końcowe interfejsu API zwracają kody stanu, podczas gdy strony internetowe uzyskują przekierowania, więc kod przeglądarki, który następuje po przekierowaniu, musi obsługiwać 401 i 403 zamiast tego.
  • Aplikacje korzystające wyłącznie z interfejsu API: Wykryte punkty końcowe interfejsu API zwracają prawidłowe kody stanu HTTP bez dodatkowej konfiguracji.

Aby zachować poprzednie działanie, wywołaj AllowCookieRedirect dla punktów końcowych, których to dotyczy, zastosuj [AllowCookieRedirect] do kontrolerów, których to dotyczy, lub włącz przełącznik Microsoft.AspNetCore.Authentication.Cookies.IgnoreRedirectMetadata dla całej aplikacji.

Testowanie punktów końcowych interfejsu API

Po uaktualnieniu do ASP.NET Core w .NET 10 sprawdź, czy punkty końcowe interfejsu API zwracają odpowiednie kody stanu:

[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);
}