dotnet / dotnet/AspNetCore.Docs

API Endpoint Auth: Verify updated for .NET 10 - Freshness

Open
#37,637 1 comment 1 reaction 2 assignees Claimed by @wadepickett View on GitHub
okr-freshness Source - Docs.ms
Dominant language
C#
Stars
13.1k
Forks
24.6k
Avg merge
1d 3h
Merged PRs (30d)
97

Description

### Description

This article needs an update for .NET 10 in several areas.

#### Review: API endpoint authentication behavior article (.NET 10)

File: `aspnetcore/security/authentication/api-endpoint-auth.md`
Commit: `a70166e740ba50ca6b28e607ad26dd750fdf6882`

Line numbers refer to the commit above. The `[ApiController]` bullet (line 22) and the SignalR bullet (line 25) were verified accurate against the shipped source and need no change.

---

#### Incorrect and must fix, Pri 1

#### Line 52 — "non-breaking" claim

> `This behavior change introduced in .NET 10 is designed to be non-breaking for existing applications:`

This directly contradicts the sibling doc in the same repo, `aspnetcore/breaking-changes/10/cookie-authentication-api-endpoints.md`, which classifies the change as a behavioral change. Any existing app combining `[ApiController]` with cookie authentication that relied on a 302 to the login page changes behavior on upgrade.

**Fix:** Rename the H2 at line 50 from "Migration considerations" to "Breaking change considerations", replace the line 52 claim, and add an `` to the breaking-change article.

#### Lines 32 and 68 — "without redirects" and the `Location` assertion

> Line 32: `- **API endpoints**: Return 401 or 403 status codes without redirects`
>
> Line 68: `Assert.False(response.Headers.Location != null); // No redirect`

The handler sets `Location` and *then* the status code. The header is not suppressed:

```csharp
// src/Security/Authentication/Cookies/src/CookieAuthenticationEvents.cs
if (IsAjaxRequest(context.Request) || IsCookieRedirectDisabledByMetadata(context.HttpContext))
{
context.Response.Headers.Location = context.RedirectUri;
context.Response.StatusCode = 401;
}
```

Line 32 is merely loose wording. Line 68 is outright false — the sample test as written would fail.

**Fix:** Change line 32 to "Return 401 or 403 status codes instead of a 302 redirect" and add a `[!NOTE]` stating that the `Location` header is still present. Rewrite line 68 to assert the header **is** present.

#### Line 23 — Minimal API detection criteria

> `- Minimal API endpoints registered with MapGet, MapPost, MapPut, MapDelete, etc.`

The registration verb is irrelevant. The metadata is added conditionally based on the handler's return type and request body:

```csharp
// src/Http/Http.Extensions/src/RequestDelegateFactory.cs
if (returnType == typeof(void) || typeof(IResult).IsAssignableFrom(returnType)) { return; }

if (returnType == typeof(string))
{
builder.Metadata.Add(ProducesResponseTypeMetadata.CreateUnvalidated(typeof(string), 200, PlaintextContentType));
}
else
{
builder.Metadata.Add(ProducesResponseTypeMetadata.CreateUnvalidated(returnType, 200, DefaultAcceptsAndProducesContentType));
if (factoryContext.JsonRequestBodyParameter is null)
{
builder.Metadata.Add(DisableCookieRedirectMetadata.Instance);
}
}
```

`MapGet("/x", () => "hello")` returns a string and void-returning handlers return early — both still **redirect**.

**Fix:** Adopt the accurate wording already used in the breaking-change doc — "Minimal API endpoints that read JSON request bodies or write JSON responses" — and add a separate bullet for endpoints using `TypedResults` return types.

#### Line 24 — "Endpoints that explicitly request JSON responses"

This implies runtime `Accept`-header content negotiation. Detection is entirely build-time metadata inference.

**Fix:** Delete the bullet; it is covered by the corrected line 23 bullets.

#### Lines 34–48 — "Configuring the behavior" section

The code block at lines 38–46 is a no-op that demonstrates nothing, and the guidance on line 48 (use `[Authorize]` with specific schemes or implement custom authentication handlers) is not the mechanism for controlling this behavior. The actual shipped opt-in/opt-out APIs appear nowhere in the article:

```csharp
// src/Http/Http.Extensions/src/CookieRedirectEndpointConventionBuilderExtensions.cs
public static TBuilder DisableCookieRedirect(this TBuilder builder) where TBuilder : IEndpointConventionBuilder
{
builder.Add(b => b.Metadata.Add(DisableCookieRedirectMetadata.Instance));
return builder;
}

public static TBuilder AllowCookieRedirect(this TBuilder builder) where TBuilder : IEndpointConventionBuilder
{
builder.Add(b => b.Metadata.Add(_allowCookieRedirectAttribute));
return builder;
}
```

There is also an `[AllowCookieRedirect]` attribute (`AttributeTargets.Method | AttributeTargets.Class`). Critically, `IAllowCookieRedirectMetadata` **always wins regardless of order** — a detail the article must state.

**Fix:** Replace lines 36–48 with working examples of `MapGroup(...).DisableCookieRedirect()`, `.AllowCookieRedirect()`, and the `[AllowCookieRedirect]` attribute on controllers.

#### Cross-file — `aspnetcore/breaking-changes/10/cookie-authentication-api-endpoints.md`, lines 13 and 108

> `Known API endpoints are identified using the new IApiEndpointMetadata ... interface`

`IApiEndpointMetadata` **does not exist in the shipped product** — searching `dotnet/aspnetcore` returns zero results. It was renamed before RTM to the `IDisableCookieRedirectMetadata` / `IAllowCookieRedirectMetadata` pair.

**Fix:** Correct both references. Add the convention-builder extensions and `AllowCookieRedirectAttribute` to the "Affected APIs" list at lines 106–110.

---

### Material gaps Pri 2

#### Missing global opt-out switch (insert after line 48)

A global `AppContext` switch restores the pre-.NET 10 behavior app-wide:

```csharp
// src/Security/Authentication/Cookies/src/CookieAuthenticationEvents.cs
private static readonly bool _ignoreCookieRedirectMetadata =
AppContext.TryGetSwitch("Microsoft.AspNetCore.Authentication.Cookies.IgnoreRedirectMetadata", out var isEnabled) && isEnabled;
```

This is the cleanest escape hatch for large apps. The breaking-change doc also omits it — lines 40–104 there show only manual event overrides, which is far more work for the same result.

**Fix:** Document the switch in both files, including the `RuntimeHostConfigurationOption` MSBuild form.

#### Missing scope caveat (insert after line 32)

`OnRedirectToLogout` and `OnRedirectToReturnUrl` check only `IsAjaxRequest` — they do **not** call `IsCookieRedirectDisabledByMetadata`. Sign-out from an API endpoint still issues a 302. Only the challenge (401) and forbid (403) paths are affected by this change.

**Fix:** Add a note clarifying the change applies to challenge and forbid only, not sign-out or return-URL redirects.

---

#### Scope

Lines 16–70 need rewriting. Only the front matter shell, lines 22 and 25, and the Related topics list survive largely intact.

### Page URL

https://learn.microsoft.com/en-us/aspnet/core/security/authentication/api-endpoint-auth?view=aspnetcore-10.0

### Content source URL

https://github.com/dotnet/AspNetCore.Docs/blob/main/aspnetcore/security/authentication/api-endpoint-auth.md

### Document ID

202ccb1a-4a6a-aa0d-9279-d2556a46294b

### Platform Id

bde9cd94-8998-5312-1ed9-3d37c49986c5

### Article author

@wadepickett

### Metadata

* ID: 202ccb1a-4a6a-aa0d-9279-d2556a46294b
* PlatformId: bde9cd94-8998-5312-1ed9-3d37c49986c5
* Service: **aspnet-core**
* Sub-service: **security**

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.