Document IPasskeyHandler and attestation state integrity/ownership - #37444
Document IPasskeyHandler and attestation state integrity/ownership#37444wadepickett with Copilot wants to merge 8 commits into
Conversation
Co-authored-by: wadepickett <10985336+wadepickett@users.noreply.github.com>
Co-authored-by: wadepickett <10985336+wadepickett@users.noreply.github.com>
Co-authored-by: Wade Pickett <wpickett@microsoft.com>
|
Testing out a copilot instruction that we may add into the repo copilot instructions or skill.md below. Moving code comments out of the code and addressing as a bulleted list after the code. Also diffentiating between explanatory comments and structural placeholder comments. |
|
@copilot: Complete the task below per the guidance in the task: Task: Move localizable explanatory comments out of C# code blocks in the passkeys articleFile: Scope: The WhyExplanatory comments inside fenced code blocks are not localized into other languages (Spanish, French, etc.). Guidance that helps the reader understand why a step matters belongs in the article prose so it gets translated. This also matches the repo's established pattern of following a code block with an What to doFor each affected C# code block:
Apply this to
Example (before → after)Before: // Protect the attestation state and store it server-side, bound to the
// current session and user. Never send the raw state to the browser.
var protector = dataProtectionProvider.CreateProtector("Passkeys.Attestation");
var protectedState = protector.Protect(optionsResult.AttestationState ?? string.Empty);
// Store 'protectedState', 'userId', and a short expiration keyed by the
// session ID in '{PROTECTED STATE STORE}'.
return TypedResults.Content(
optionsResult.CreationOptionsJson, contentType: "application/json");
});After: var protector = dataProtectionProvider.CreateProtector("Passkeys.Attestation");
var protectedState = protector.Protect(optionsResult.AttestationState ?? string.Empty);
// Store 'protectedState', 'userId', and a short expiration in
// '{PROTECTED STATE STORE}', keyed by the session ID.
return TypedResults.Content(
optionsResult.CreationOptionsJson, contentType: "application/json");
});The preceding code:
After making the changes
|
Co-authored-by: wadepickett <10985336+wadepickett@users.noreply.github.com>
... Done in d2ea7fc. Moved the explanatory comments out of both registration endpoint code blocks into |
There was a problem hiding this comment.
Pull request overview
This PR expands the passkeys documentation to cover direct use of IPasskeyHandler<TUser> and the security responsibilities that come with app-managed attestation state, addressing the documented account-takeover risk when state is round-tripped through the browser without integrity protections (Fixes #37437).
Changes:
- Added a new section describing when and how to use
IPasskeyHandler<TUser>vs. the recommendedSignInManagerpath. - Documented what attestation state is, why it’s security-sensitive, and the integrity/ownership requirements apps must enforce.
- Added a minimal API example showing server-side protected storage of attestation state and revalidation on return, plus updated front matter (
ai-usage,ms.date).
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-authored-by: wadepickett <10985336+wadepickett@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Without checking protectedState the "no state exists" path throws a 500 instead of a graceful rejection. Changing code to match the documented behavoir.
The passkeys article only documented the
SignInManagerregistration path, leavingIPasskeyHandler<TUser>and the attestation state undocumented. This is a security gap: apps using the handler directly take over storing the state, which is unsigned plain JSON that decides which account a new passkey attaches to — round-tripping it through the browser unprotected enables account takeover by editing the user ID.Changes
Added a new H2, "Customize passkey handling with
IPasskeyHandler<TUser>", toaspnetcore/security/authentication/passkeys/index.md, between "Custom origin validation" and "Registration flow" (customization sections ordered least-to-most invasive).SignInManager; explains it stores/clears state in a data-protected auth cookie.MakeCreationOptionsAsync,MakeRequestOptionsAsync,PerformAttestationAsync,PerformAssertionAsync) and when to implement/wrap it.CreationOptionsJson(browser) vs.AttestationState(app-held), carrying the challenge andPasskeyUserEntity;[!WARNING]on the lack of integrity protection.SignInManagerreference behavior.PasskeyAttestationResult.UserEntity, and clear the state.IPasskeyHandler<TUser>is registered and how to replace it.ai-usage: ai-assisted, updatedms.date.All API references use verified
<xref>IDs sourced from the product source. No new file or redirection needed.IPasskeyHandlerand how to validate attestation state integrity and ownership #37437Internal previews