Fix Blazor passkey registration under CsrfProtection - #67589
Conversation
Updated the scaffolded passkey endpoints to opt into the new CSRF validation path by applying [RequireAntiforgeryToken] to both POST endpoints, so they now require the middleware-provided antiforgery validation. This is covered in commit fa8f11e. |
|
Reviewed against Blocking concern: after this change these two endpoints appear to have no CSRF protection at all.
Minimal-API endpoints acquire that metadata ( This is exactly the framework's own documented behavior: in Net: the explicit Test adequacy: Refs #67588. |
|
@copilot address the feedback. Make sure that we have requires antiforgery and that we validate the feature manually inside the handler |
Implemented the follow-up so the scaffolded passkey endpoints now both opt into antiforgery validation via [RequireAntiforgeryToken] and explicitly check the middleware’s IAntiforgeryValidationFeature before proceeding. This is covered in commit 485a3f1. |
|
|
||
| } | ||
|
|
||
| public class BlazorWebTemplatePasskeyAntiforgeryTest |
There was a problem hiding this comment.
This test proves nothing. Remove it.
Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com>
Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com>
Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com>
Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com>
9964af6 to
915b123
Compare
There was a problem hiding this comment.
Pull request overview
Updates the Blazor Web (Individual auth) project template passkey flow to work when the app uses fetch-metadata based CsrfProtection, by removing the legacy antiforgery token/header path and opting the passkey option endpoints into middleware-based CSRF validation.
Changes:
- Removed antiforgery token acquisition/plumbing from the
PasskeySubmitcomponent and its companion JavaScript so passkey option requests no longer send the legacyrequestverificationtokenheader. - Updated
/Account/PasskeyCreationOptionsand/Account/PasskeyRequestOptionstemplate endpoints to opt into antiforgery/CSRF middleware via[RequireAntiforgeryToken]and to surface validation failures as 400 responses. - Minor cleanup in the Blazor template test file (unused using / whitespace).
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWebCSharp.1/Components/Account/Shared/PasskeySubmit.razor.js | Stops sending legacy antiforgery headers when fetching passkey options. |
| src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWebCSharp.1/Components/Account/Shared/PasskeySubmit.razor | Removes antiforgery token generation/injection from the passkey submit component. |
| src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWebCSharp.1/Components/Account/IdentityComponentsEndpointRouteBuilderExtensions.cs | Opts passkey option endpoints into middleware-based CSRF validation and checks the middleware verdict. |
| src/ProjectTemplates/test/Templates.Blazor.Tests/BlazorWebTemplateTest.cs | Removes an unused using / whitespace-only change. |
The scaffolded Blazor template requires all .razor files under Web.ProjectTemplates/content to be UTF-8-BOM-encoded (enforced by ByteOrderMarkTest.RazorFilesInWebProjects_ShouldContainBOM). The rewrite in this PR stripped the BOM; restore it. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Under the default-on CsrfProtectionMiddleware (with app.UseAntiforgery() removed by #67119), the hidden __RequestVerificationToken fields rendered by <AntiforgeryToken /> are never validated. Blazor SSR form binding and minimal-API [FromForm] parameters auto-attach IAntiforgeryMetadata, so those endpoints stay CSRF-protected by the new middleware. - Remove 12 dead <AntiforgeryToken /> components across 10 razor files. - Add explicit [RequireAntiforgeryToken] to /DownloadPersonalData (JSON minimal-API POST with no [FromForm] auto-attach). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This endpoint reads no form body ([FromServices] only), so neither FormFeature nor RequestDelegateFactory form binding will reject on a failed IAntiforgeryValidationFeature verdict. Add an explicit check matching the pattern used by the sibling PasskeyCreationOptions and PasskeyRequestOptions endpoints, so cross-site POSTs are rejected with 400 instead of silently returning the user's personal data. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Switch the three explicit-check endpoints (PasskeyCreationOptions,
PasskeyRequestOptions, DownloadPersonalData) from `is { IsValid: false }`
to `is not { IsValid: true }` so that a null feature (e.g. when
CsrfProtectionMiddleware is skipped for any reason) is also rejected.
The endpoints explicitly opted in via [RequireAntiforgeryToken], so
proceeding without a validated verdict would violate that intent.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The prior commit used `is not { IsValid: true } antiforgeryValidationFeature`
which does not definitely-assign the pattern variable in the negated
branch. Extract the feature into an explicit local before the check to
compile cleanly and keep fail-closed semantics on null / IsValid=false.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…68277) The failures tracked by #66708 were a real regression, not flakiness. They were fixed by #67589 (Fix Blazor passkey registration under CsrfProtection). The test has been green on main ever since. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ff84308e-d869-4c91-aa30-36e2bde27627
Summary of the changes (Less than 80 chars)
Make Blazor passkey registration work with CsrfProtection
Description
The scaffolded Blazor Individual auth passkey flow was still using the classic antiforgery token-cookie path. Under the new fetch-metadata CsrfProtection middleware, that cookie is never issued, so the passkey option endpoints returned 500s before the browser could complete registration.
IAntiforgery.ValidateRequestAsyncchecks from the scaffolded/Account/PasskeyCreationOptionsand/Account/PasskeyRequestOptionsendpoints so they rely on the middleware-provided CSRF protection.requestverificationtokenheader for these requests.