Skip to content

feat(passkey): create passkey authentication methods#20216

Open
MagentaManifold wants to merge 1 commit intomainfrom
FXA-13062
Open

feat(passkey): create passkey authentication methods#20216
MagentaManifold wants to merge 1 commit intomainfrom
FXA-13062

Conversation

@MagentaManifold
Copy link
Contributor

Because

  • we want passkey authentication methods

This pull request

  • creates passkey authentication methods

Issue that this pull request solves

Closes: FXA-13062

Checklist

Put an x in the boxes that apply

  • My commit is GPG signed.
  • If applicable, I have modified or added tests which pass locally.
  • I have added necessary documentation (if appropriate).
  • I have verified that my changes render correctly in RTL (if appropriate).

Screenshots (Optional)

Please attach the screenshots of the changes made in case of change in user interface.

Other information (Optional)

Any other information that is important to this pull request.

@MagentaManifold MagentaManifold requested a review from a team as a code owner March 18, 2026 19:46
@MagentaManifold MagentaManifold requested a review from Copilot March 18, 2026 19:47
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds high-level Passkey (WebAuthn) authentication/assertion flows to the passkey library, integrating challenge lifecycle management + SimpleWebAuthn verification.

Changes:

  • Implemented generateAuthenticationChallenge() to issue assertion options (optionally restricted to a user’s credentials).
  • Implemented verifyAuthenticationResponse() to validate challenges, verify assertions, update passkey counters/backup state, and emit metrics/logs.
  • Added comprehensive unit tests for the new service methods (including signCount rollback logging).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
libs/accounts/passkey/src/lib/passkey.service.ts Adds authentication challenge generation + assertion verification orchestration, including metrics/logging and passkey updates.
libs/accounts/passkey/src/lib/passkey.service.spec.ts Adds unit test coverage for the new authentication flows and key error paths.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Because:

* we want passkey authentication methods

This commit:

* creates passkey authentication methods

Closes #FXA-13062
const passkey =
await this.passkeyManager.findPasskeyByCredentialId(credentialId);
if (!passkey) {
throw AppError.passkeyNotFound();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add a metric and log here so we can monitor this condition.

'authentication'
);
if (!storedChallenge) {
throw AppError.passkeyChallengeExpired();
Copy link
Contributor

@dschom dschom Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto ad metric & log. Basically if we error out, let's record an passkey.authentication.fail metric. There's a few spots where this could be done.

allowCredentials = passkeys.map((p) => p.credentialId);
}

return await generateAuthenticationOptions(this.config, {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be worth while to record a metric for the number of these challenges that we create.

throw AppError.passkeyChallengeExpired();
}

if (expectedUid && !passkey.uid.equals(expectedUid)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check can go before the call to const storedChallenge = await this.challengeManager.validateChallenge(...). Doing this before would be better, since the call to validateChallenge burns the challenge (ie uses GETDEL).

if (!updated) {
this.log?.error('passkey.updateAfterAuth.failed', {
uid: passkey.uid.toString('hex'),
credentialId: credentialId.toString('hex'),
Copy link
Contributor

@dschom dschom Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we log the newSignCount since that could cause the query to fail?`

uid: passkey.uid.toString('hex'),
credentialId: credentialId.toString('hex'),
});
throw AppError.passkeyNotFound();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's more likely the update fails due to a write conflict on signCount, in which case this error is misleading. That said, it might be good enough. Perhaps the calling code won't care either way.

allowCredentials: [],
});
});
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add a case for what happens if the UID doesn't match?

newSignCount,
backupState
);
if (!updated) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we have a test covering this when updated is false.

Copy link
Contributor

@dschom dschom left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good! I think this comment is important, and adding a couple more metrics so we can keep tabs on this better.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants