-
Notifications
You must be signed in to change notification settings - Fork 81
Description
Description
By default, unauthenticated and unauthorized requests made to known API endpoints protected by cookie authentication now result in 401 and 403 responses rather than redirecting to a login or access denied URI.
Known API Endpoints are identified using the new IDisableCookieRedirectMetadata
interface (previously IApiEndpointMetadata
), and metadata implementing this interface is added automatically to the following:
[ApiController]
endpoints- Minimal API endpoints that read JSON request bodies or write JSON responses
- Endpoints using
TypedResults
return types - SignalR endpoints
Update: Additional Workarounds and Metadata
After feedback on the initial change, we've added more flexible ways to work around issues caused by this breaking change:
- New metadata interface:
IDisableCookieRedirectMetadata
replacesIApiEndpointMetadata
. - Opt-in attribute & metadata: You can now use the
[AllowCookieRedirect]
attribute or implement theIAllowCookieRedirectMetadata
interface on your endpoints to explicitly allow cookie authentication redirects, even on endpoints that would otherwise suppress them. - Global opt-out AppContext Switch: You can set the AppContext switch
Microsoft.AspNetCore.Authentication.Cookies.IgnoreRedirectMetadata
totrue
to revert to the old behavior globally and ignore the new metadata-based suppression. This is particularly useful for large apps or frameworks (like UmbracoCMS) that need a quick mitigation.
Version
.NET 10 Preview 7
Previous behavior
The cookie authentication handler would redirect unauthenticated and unauthorized requests to a login or access denied URI by default for all requests other than XMLHttpRequests (XHRs).
New behavior
Unauthenticated and unauthorized requests made to known API endpoints will result in 401 and 403 responses rather than redirecting to a login or access denied URI. XHRs continue to result in 401 and 403 responses regardless of the target endpoint.
Type of breaking change
- Binary incompatible: Existing binaries may encounter a breaking change in behavior, such as failure to load or execute, and if so, require recompilation.
- Source incompatible: When recompiled using the new SDK or component or to target the new runtime, existing source code may require source changes to compile successfully.
- Behavioral change: Existing binaries may behave differently at run time.
Reason for change
dotnet/aspnetcore#9039 was highly requested, and redirecting unauthenticated requests to a login page doesn't usually make sense for API endpoints which typically rely on 401 and 403 status codes rather than HTML redirects to communicate auth failures.
Recommended action
To always redirect (fully disable new behavior):
Recommended for frameworks/applications needing an immediate opt-out or full legacy compatibility:
Set the following AppContext switch (e.g., in your app's runtimeconfig.json or programmatically at startup):
{
"runtimeOptions": {
"configProperties": {
"Microsoft.AspNetCore.Authentication.Cookies.IgnoreRedirectMetadata": true
}
}
}
Or, set programmatically before building the host:
AppContext.SetSwitch("Microsoft.AspNetCore.Authentication.Cookies.IgnoreRedirectMetadata", true);
To allow redirects on specific endpoints:
Apply the [AllowCookieRedirect]
attribute to controllers or endpoints that should preserve cookie-based redirects, or implement the IAllowCookieRedirectMetadata
interface.
[AllowCookieRedirect]
public class MyLegacyApiController : Controller { ... }
To revert to the exact previous handler logic (redirect except for XHRs):
You can override the events with the following logic:
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;
};
});
To always redirect regardless of endpoint type or XHR:
Override the events to always redirect:
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;
};
});
References
- Issue: ApiController redirects to login page dotnet/aspnetcore#9039
- PR (original breaking change): Avoid cookie login redirects for known API endpoints dotnet/aspnetcore#62816
- PR (additional opt-outs & metadata): Address API review feedback for what was IApiEndpointMetadata dotnet/aspnetcore#63283
Affected APIs
Microsoft.AspNetCore.Http.Metadata.IDisableCookieRedirectMetadata
Microsoft.AspNetCore.Http.Metadata.IAllowCookieRedirectMetadata
[AllowCookieRedirect]
attribute- CookieAuthenticationEvents.RedirectToLogin
- CookieAuthenticationEvents.RedirectToAccessDenied