Correct FinishPasskeyLogin return-value handling in passkey authentication flow#352
Merged
Merged
Conversation
Copilot
AI
changed the title
[WIP] Review the second return type of FinishPasskeyLogin in v0.17.x godoc
Correct May 23, 2026
FinishPasskeyLogin return-value handling in passkey authentication flow
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes the passkey authentication completion flow to persist the correct, updated WebAuthn credential data when using github.com/go-webauthn/webauthn v0.17.x (where FinishPasskeyLogin returns the updated credential as the second return value).
Changes:
- Corrected return-value handling in
PasskeyHandler.FinishAuthenticationto read the updated credential from the second return position ofFinishPasskeyLogin. - Ensures credential persistence (
json.Marshal+PasskeyStore.UpdateCredentialData) operates on the updated credential object rather than the user object.
Show a summary per file
| File | Description |
|---|---|
handler/passkey.go |
Fixes FinishPasskeyLogin return-value mapping so credential counter persistence uses the updated credential. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 1/1 changed files
- Comments generated: 1
…eceives updated signCount Adds a webAuthnProvider interface so FinishPasskeyLogin can be mocked in tests, then adds TestPasskey_finishAuthentication_updatesCredentialDataWithSignCount to assert that the updated credential (2nd return value from FinishPasskeyLogin) is the value persisted via UpdateCredentialData, preventing silent regression if the credential update path is broken by a future library upgrade.
github-actions Bot
added a commit
that referenced
this pull request
May 24, 2026
…ting section - FinishRegistration and FinishAuthentication both return 404 Not Found when the user is not found in the store; these cases were missing from the HTTP status codes table. - Clarify that FinishRegistration's 500 does not include the user-fetch path (that now surfaces as 404). - Add 'Testing and custom WebAuthn providers' section documenting the unexported webAuthnProvider interface introduced in #352, including the requirement that mocks return a non-nil *webauthn.Credential so the counter-update path is exercised correctly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
veverkap
added a commit
that referenced
this pull request
May 24, 2026
… testing section (#356) * docs(passkeys): add missing 404 status codes and webAuthnProvider testing section - FinishRegistration and FinishAuthentication both return 404 Not Found when the user is not found in the store; these cases were missing from the HTTP status codes table. - Clarify that FinishRegistration's 500 does not include the user-fetch path (that now surfaces as 404). - Add 'Testing and custom WebAuthn providers' section documenting the unexported webAuthnProvider interface introduced in #352, including the requirement that mocks return a non-nil *webauthn.Credential so the counter-update path is exercised correctly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs(passkeys): add user-fetch store error to FinishRegistration 500 row The 500 status-code row for FinishRegistration omitted the case where Users.FindByID returns a non-ErrNotFound error (e.g. DB timeout), which still produces a 500. The Observability table already listed this log event, making the status-code table self-contradictory. * docs(passkeys): correct nil-credential guidance in webAuthnProvider section The previous wording implied returning nil from FinishPasskeyLogin would skip the counter-update path. In practice json.Marshal(nil) succeeds with "null", so UpdateCredentialData is still called with corrupt data. Updated to warn that mocks must return a valid non-nil credential. --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Patrick Veverka <veverkap@users.noreply.github.com>
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.
PasskeyHandler.FinishAuthenticationwas using the wrong return value fromgo-webauthnFinishPasskeyLoginin v0.17.x context. The handler treated the first return as the updated credential, but the credential is returned as the second value.Return-value mapping fix
FinishAuthenticationto read the credential from the second return position ofFinishPasskeyLogin.Credential counter persistence correctness
json.MarshalandPasskeyStore.UpdateCredentialDatais now the actual updated WebAuthn credential, not the user object.Greptile Summary
This PR fixes a return-value swap bug in
FinishAuthentication:FinishPasskeyLoginreturns(webauthn.User, *webauthn.Credential, error), but the handler was binding the credential variable to the first return position (theUser), causing each login to serialize and persist the user object instead of the updated credential. The fix correctly reads the credential from the second position.handler/passkey.go): swapsupdatedCred, _, errto_, updatedCred, errsojson.MarshalandUpdateCredentialDatareceive the actual*webauthn.Credentialwith the post-ceremony sign count.handler/passkey.go):PasskeyHandler.WebAuthnis changed from the concrete*webauthn.WebAuthnto a newwebAuthnProviderinterface, enabling the mock-based test that directly verifies the persistence path.handler/passkey_test.go):TestPasskey_finishAuthentication_updatesCredentialDataWithSignCountdrives the discoverable-user handler through the mock and asserts that the marshaled JSON written toUpdateCredentialDatacarries the expectedSignCount.Confidence Score: 5/5
Safe to merge — the change is a targeted one-line swap that corrects which return value gets persisted, with a new test that directly validates the fixed path end-to-end.
The change touches a single assignment in
FinishAuthenticationand introduces an interface extraction for testability. The regression test drives the discoverable-user handler through the mock and asserts the exactSignCountvalue written to the store, leaving no ambiguity about the fix. No error-handling paths are altered, and the rest of the authentication flow (token issuance, cookie setting) is unchanged.No files require special attention.
Important Files Changed
FinishPasskeyLoginreturn-value bindings so the*webauthn.Credential(not thewebauthn.User) is marshaled and persisted; also extracts awebAuthnProviderinterface for testability.mockWebAuthnProviderand a focused regression test that injects a knownSignCount=42credential and asserts the store receives it, directly covering the fixed code path.Sequence Diagram
sequenceDiagram participant Client participant FinishAuthentication participant DiscoverableUserHandler participant PasskeyStore participant WebAuthnProvider Client->>FinishAuthentication: "POST /passkeys/auth/finish?session_id=..." FinishAuthentication->>PasskeyStore: GetAndDeleteChallenge(sessionID) PasskeyStore-->>FinishAuthentication: challengeData FinishAuthentication->>WebAuthnProvider: FinishPasskeyLogin(handler, sessionData, r) WebAuthnProvider->>DiscoverableUserHandler: handler(rawID, userHandle) DiscoverableUserHandler->>PasskeyStore: FindCredentialByCredentialID(credID) PasskeyStore-->>DiscoverableUserHandler: credential record DiscoverableUserHandler->>PasskeyStore: ListCredentialsByUser(userID) PasskeyStore-->>DiscoverableUserHandler: existing creds DiscoverableUserHandler-->>WebAuthnProvider: passkeyUser WebAuthnProvider-->>FinishAuthentication: (_, updatedCred, nil) FIXED FinishAuthentication->>PasskeyStore: UpdateCredentialData(authedUserID, authedCredentialID, json(updatedCred)) PasskeyStore-->>FinishAuthentication: ok FinishAuthentication->>Client: 200 OKReviews (2): Last reviewed commit: "Merge branch 'main' into copilot/review-..." | Re-trigger Greptile