Don't apply the CSRF verdict to remote authentication callbacks - #68669
Merged
wtgodbe merged 2 commits intoAug 21, 2026
Conversation
A remote provider's callback (OIDC response_mode=form_post, WS-Federation) is a cross-site form POST by protocol design, so the auto-injected CSRF protection records an invalid IAntiforgeryValidationFeature verdict for it. The handler then throws while reading its own callback body, before any of its events can run, so apps have no way to opt out. Suppress the verdict while a remote handler owns the request, and restore it if the handler declines so the rest of the pipeline still sees it. Fixes dotnet#68666 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: cb987098-3301-465b-9a3d-2e63aabf43bd
DeagleGross
marked this pull request as ready for review
August 21, 2026 07:57
DeagleGross
requested review from
Youssef1313,
cincuranet,
halter73 and
rokonec
as code owners
August 21, 2026 07:57
DeagleGross
temporarily deployed
to
copilot-pat-pool
August 21, 2026 07:57 — with
GitHub Actions
Inactive
DeagleGross
temporarily deployed
to
copilot-pat-pool
August 21, 2026 07:58 — with
GitHub Actions
Inactive
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes a regression where automatic cross-origin CSRF protection records an invalid IAntiforgeryValidationFeature on remote authentication callback requests (e.g., OIDC response_mode=form_post), causing ReadFormAsync to throw before the authentication handler can process the callback.
Changes:
- Introduces a shared helper to temporarily suppress an invalid antiforgery/CSRF verdict while a remote authentication handler processes its owned callback request, restoring it if the handler declines.
- Applies the suppression in
RemoteAuthenticationHandler(covers standard remote callbacks) and inOpenIdConnectHandlerfor its additional callback endpoints. - Adds integration tests validating that cross-site callback POSTs can be read by the handler, and that the invalid verdict is restored when the handler skips the request.
Show a summary per file
| File | Description |
|---|---|
| src/Shared/RemoteAuthenticationAntiforgery.cs | Adds shared helper to suppress/restore invalid IAntiforgeryValidationFeature during remote handler execution. |
| src/Security/Authentication/Core/src/RemoteAuthenticationHandler.cs | Wraps callback handling in the suppression helper (covers CallbackPath). |
| src/Security/Authentication/Core/src/Microsoft.AspNetCore.Authentication.csproj | Links the new shared helper into the authentication core build. |
| src/Security/Authentication/OpenIdConnect/src/OpenIdConnectHandler.cs | Uses the helper for OIDC-specific callback paths that can read form_post bodies. |
| src/Security/Authentication/OpenIdConnect/src/Microsoft.AspNetCore.Authentication.OpenIdConnect.csproj | Links the new shared helper into the OpenIdConnect build. |
| src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/RemoteAuthenticationCsrfTests.cs | Adds tests proving the handler can read cross-site callback forms and that downstream protection is preserved when skipping. |
Review details
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
- Files reviewed: 6/6 changed files
- Comments generated: 0
- Review effort level: Lite
javiercn
approved these changes
Aug 21, 2026
wtgodbe
added a commit
that referenced
this pull request
Aug 22, 2026
* [SignalR] Reject duplicate SignalR upload stream IDs (#68525) (#68638) * Reject duplicate SignalR upload stream IDs * Simplify upload stream ownership cleanup * Avoid upload stream ownership allocations * Simplify upload stream registration ownership * Defer upload stream reader creation * Dispose cancellation source after binding failure * Reuse upload stream test helper --------- Co-authored-by: Javier Calvarro Nelson <jacalvar@microsoft.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 82e97f5a-a052-4dbe-9cf1-b62f45cf7ee2 * Honor all sign-in confirmation requirements after registration (#68631) (#68655) Co-authored-by: Brennan <brecon@microsoft.com> * Preserve BadHttpRequestException status codes (#68632) (#68649) * Preserve BadHttpRequestException status codes * Preserve exception handler 404 safeguard --------- Co-authored-by: Stephen Halter <halter73@gmail.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * SignInManager: return SignInResult.Failed for expired passkey session challenge (#67539) (#68654) Co-authored-by: Grant Totinov <granttotinov604@gmail.com> * Don't apply the CSRF verdict to remote authentication callbacks (#68669) * Don't apply the CSRF verdict to remote authentication callbacks A remote provider's callback (OIDC response_mode=form_post, WS-Federation) is a cross-site form POST by protocol design, so the auto-injected CSRF protection records an invalid IAntiforgeryValidationFeature verdict for it. The handler then throws while reading its own callback body, before any of its events can run, so apps have no way to opt out. Suppress the verdict while a remote handler owns the request, and restore it if the handler declines so the rest of the pipeline still sees it. Fixes #68666 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: cb987098-3301-465b-9a3d-2e63aabf43bd * test both antiforgery & csrf --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: cb987098-3301-465b-9a3d-2e63aabf43bd * Use model display names in Blazor input parsing errors (#68667) (#68688) * Use display attributes in input parsing errors * Address test coverage feedback from review. * Apply dedup cleanup from feedback. Co-authored-by: Ilona Tomkowicz <32700855+ilonatommy@users.noreply.github.com> * [release/11.0-rc1] Extract IsAuthenticated helper method (#68658) * Extract IsAuthenticated helper method Co-authored-by: Youssef1313 <31348972+Youssef1313@users.noreply.github.com> * Reorder using Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Youssef1313 <31348972+Youssef1313@users.noreply.github.com> * Use SecurityHelper for authentication revalidation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Youssef1313 <31348972+Youssef1313@users.noreply.github.com> --------- Co-authored-by: Youssef1313 <youssefvictor00@gmail.com> Co-authored-by: Youssef1313 <31348972+Youssef1313@users.noreply.github.com> Co-authored-by: Milos Kotlar <kotlarmilos@gmail.com> * Fix InitialItemIndex viewport underfill for small items in big container or on window resize (#67936) (#68689) Co-authored-by: Ilona Tomkowicz <32700855+ilonatommy@users.noreply.github.com> * fix nullable<union> for openapi gen (#68665) --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Javier Calvarro Nelson <jacalvar@microsoft.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Co-authored-by: Brennan <brecon@microsoft.com> Co-authored-by: Stephen Halter <halter73@gmail.com> Co-authored-by: Grant Totinov <granttotinov604@gmail.com> Co-authored-by: Korolev Dmitry <dmkorolev@microsoft.com> Co-authored-by: Ilona Tomkowicz <32700855+ilonatommy@users.noreply.github.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: Youssef1313 <youssefvictor00@gmail.com> Co-authored-by: Youssef1313 <31348972+Youssef1313@users.noreply.github.com> Co-authored-by: Milos Kotlar <kotlarmilos@gmail.com> Co-authored-by: William Godbe <wigodbe@microsoft.com> Copilot-Session: 82e97f5a-a052-4dbe-9cf1-b62f45cf7ee2 Copilot-Session: cb987098-3301-465b-9a3d-2e63aabf43bd
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #68666.
A Blazor Web App using
AddOpenIdConnectfails the sign-in callback withInvalidOperationException: This form is being accessed with a failed antiforgery validationthrown fromFormFeature.ReadFormAsync, insideOpenIdConnectHandler.HandleRemoteAuthenticateAsync.How this worked in .NET 10 with antiforgery
Token antiforgery is per-endpoint:
AntiforgeryMiddlewareonly validates when the matched endpoint carriesIAntiforgeryMetadatawithRequiresValidation: true, which Blazor attaches to each routable component endpoint inRazorComponentEndpointFactory./signin-oidcis not a component endpoint, so it never carried that metadata. More importantly,UseAntiforgery()sits afterUseAuthentication(), andRemoteAuthenticationHandlershort-circuits the callback as anIAuthenticationRequestHandler— so the antiforgery middleware never ran for the callback at all.Why CSRF protection triggers now
CsrfProtectionMiddlewareis auto-injected and runs in the post-routing pipeline, whichEndpointRoutingMiddlewareinvokes immediately after matching — i.e. beforeUseAuthentication(), so the auth handler no longer short-circuits ahead of it. It recordsIAntiforgeryValidationFeature { IsValid = false }plus anHttpContext.Itemsmarker instead of rejecting the request (#67082), deferring the failure to whoever reads the body. For the callback that consumer is the OIDC handler itself, and it reads the form beforeOnMessageReceivedfires, so applications have no way to opt out.Why the callback is always a cross-site request
With
response_mode=form_postthe identity provider returns the response as a top-level auto-submitted form POST from its own origin toCallbackPath, so the browser always sendsSec-Fetch-Site: cross-site. That is the exact shape the Fetch Metadata algorithm classifies as a CSRF attempt, and nothing about a legitimate callback distinguishes it at the HTTP layer.form_postis the default forresponse_typevalues containingid_tokenand is what Entra ID andMicrosoft.Identity.Webuse, so this fails deterministically rather than intermittently. The same applies to WS-Federation and to the OIDCRemoteSignOutPath.Why suppressing the verdict here is safe
The callback carries its own forgery protection:
stateround-trips a data-protectedAuthenticationPropertiespayload whose correlation id must match the.AspNetCore.Correlation.*cookie, andValidateCorrelationIdrejects the request when it doesn't. Nonce validation forid_tokenand PKCE for the code flow apply on top of that, none of which an attacker can forge. The verdict is suppressed only while the handler owns the request and is restored when it declines (SkipHandler/SkipUnrecognizedRequests/ exception), so anything the handler hands back to the pipeline still sees the original verdict.