feat: support multiple passkeys per user - #11
Conversation
There was a problem hiding this comment.
📋 Review Summary
This pull request adds support for multiple passkeys per user across registration, authentication, and session handling. It correctly prevents duplicate authenticator registrations via excludeCredentials, maintains a collection of passkeys under the user record in KV storage, updates counters on verification, and introduces comprehensive unit tests for multi-passkey scenarios.
🔍 General Feedback
- The multi-passkey implementation is clean and handles backwards compatibility well for legacy user records missing the
passkeysarray. - Consider adding
test/test_passkeys_multi.jstopackage.jsonscripts (e.g. as a pre-test or unit test step in CI) so that these unit tests are executed automatically alongside integration tests during workflow runs.
📊 Token Usage & Cost Efficiency
| Metric | Value |
|---|---|
| Input Tokens (uncached) | 6,739 |
| Input Tokens (cached) | 23,618 (⚡ 77.0% cached) |
| PR Comments History Tokens | 313 |
| Output Tokens | 182 |
| Total Session Tokens | 33,225 |
| Cost (uncached input) | $0.0053 |
| Cost (cached input) | $0.0018 |
| Cost (output) | $0.0007 |
| Estimated Total Cost | $0.0077 |
Gemini 3.7 Flash: introductory rate $0.75/$3.75 per 1M applied; reverts to $1.5/$7.5 after 2026-12-31.
Gemini 3.7 Flash: standard tier; batch and flex are half again, priority is higher.
Cache reads are priced here, but context-cache STORAGE, where the model charges for it separately per token-hour, is not included, so the figure can run slightly low.
There was a problem hiding this comment.
📋 Review Summary
This pull request introduces support for multiple passkeys per user, correctly populating excludeCredentials during registration to prevent duplicate authenticator enrollment. It also ensures user records maintain an array of passkeys and updates counters appropriately upon successful authentication, backed by dedicated multi-passkey unit tests.
🔍 General Feedback
- The implementation of
excludeCredentialsinPasskeys.new()conforms well with WebAuthn best practices to prevent users from accidentally re-registering an already bound authenticator. - Handling of legacy user records lacking the
passkeysarray is robust acrossnew(),create(), andcheck()methods. - The addition of
test/test_passkeys_multi.jsprovides good unit coverage for both new and existing credential scenarios.
📊 Token Usage & Cost Efficiency
| Metric | Value |
|---|---|
| Input Tokens (uncached) | 3,668 |
| Input Tokens (cached) | 27,584 (⚡ 87.4% cached) |
| PR Comments History Tokens | 313 |
| Output Tokens | 194 |
| Total Session Tokens | 35,187 |
| Cost (uncached input) | $0.0030 |
| Cost (cached input) | $0.0021 |
| Cost (output) | $0.0007 |
| Estimated Total Cost | $0.0058 |
Gemini 3.7 Flash: introductory rate $0.75/$3.75 per 1M applied; reverts to $1.5/$7.5 after 2026-12-31.
Gemini 3.7 Flash: standard tier; batch and flex are half again, priority is higher.
Cache reads are priced here, but context-cache STORAGE, where the model charges for it separately per token-hour, is not included, so the figure can run slightly low.
There was a problem hiding this comment.
📋 Review Summary
This pull request introduces support for multiple passkeys per user account and updates the registration flow to populate excludeCredentials. Existing user credentials are now stored in an array under the user profile in KV, preventing duplicate registrations for already registered authenticators while updating credential counters across authentication events. The changes are accompanied by dedicated unit tests and a major version bump to 5.0.0.
🔍 General Feedback
- Great implementation of multiple passkey support and authenticator exclusion. The data structures handle backwards compatibility gracefully for legacy user records missing the
passkeysproperty. - Comprehensive unit tests in
test/test_passkeys_multi.jseffectively cover legacy record migration, exclusion list population, and count checks.
📊 Token Usage & Cost Efficiency
| Metric | Value |
|---|---|
| Input Tokens (uncached) | 27,228 |
| PR Comments History Tokens | 313 |
| Output Tokens | 177 |
| Total Session Tokens | 29,793 |
| Cost (uncached input) | $0.0207 |
| Cost (output) | $0.0007 |
| Estimated Total Cost | $0.0213 |
Gemini 3.7 Flash: introductory rate $0.75/$3.75 per 1M applied; reverts to $1.5/$7.5 after 2026-12-31.
Gemini 3.7 Flash: standard tier; batch and flex are half again, priority is higher.
There was a problem hiding this comment.
📋 Review Summary
This pull request adds support for multiple passkeys per user account and prevents re-registering existing authenticators by populating excludeCredentials during registration. It also introduces comprehensive unit tests for multi-passkey workflows and applies Prettier code formatting across the repository.
🔍 General Feedback
- The implementation for multiple passkey registration cleanly handles legacy user records missing the
passkeysarray. - The addition of
test/test_passkeys_multi.jswith isolated KV mocking provides good test coverage for the new multi-passkey logic. - Updating credential counter values in both
passkeys-{id}andusers-{id}records keeps authentication metadata consistent.
📊 Token Usage & Cost Efficiency
| Metric | Value |
|---|---|
| Input Tokens (uncached) | 30,537 |
| PR Comments History Tokens | 313 |
| Output Tokens | 173 |
| Total Session Tokens | 33,698 |
| Cost (uncached input) | $0.0231 |
| Cost (output) | $0.0006 |
| Estimated Total Cost | $0.0238 |
Gemini 3.7 Flash: introductory rate $0.75/$3.75 per 1M applied; reverts to $1.5/$7.5 after 2026-12-31.
Gemini 3.7 Flash: standard tier; batch and flex are half again, priority is higher.
Summary
Adds multi-passkey support per user:
Passkeys.new: PopulatesexcludeCredentialsfrom the user's existing passkeys in KV to prevent registering duplicates of the same authenticator.Passkeys.create: Appends the new passkey touser.passkeys(or updates an existing credential by ID) instead of overwriting the array.Passkeys.verify: Updates passkey counters in bothpasskeys-${id}andusers-${userId}.test/test_passkeys_multi.jstesting single, multi-passkey, and legacy user states.