@@ -13,20 +13,20 @@ This document provides detailed information about the passkey data model and fie
1313
1414## Quick Reference
1515
16- | Field | Type | Required | Description |
17- | -------------- | ------------------ | --------------------------- | -- --------------------------------------- |
18- | uid | Buffer(16) | Yes | User ID (FK to accounts.uid) |
19- | credentialId | Buffer(1-1023) | Yes | WebAuthn credential ID |
20- | publicKey | Buffer | Yes | COSE-encoded public key |
21- | signCount | number | Yes | Signature counter (default 0) |
22- | transports | string \| null | No | JSON array of transport types |
23- | aaguid | Buffer(16) \| null | No | Authenticator AAGUID |
24- | name | string \| null | No | Friendly name (recommended auto-generate) |
25- | createdAt | number | Yes | Unix timestamp (ms) |
26- | lastUsedAt | number \| null | Field: Yes, Value: Nullable | Last auth timestamp (ms) |
27- | backupEligible | boolean\* | No (default: 0) | Can be backed up |
28- | backupState | boolean\* | No (default: 0) | Is currently backed up |
29- | prfEnabled | boolean\* | No (default: 0) | PRF extension enabled |
16+ | Field | Type | Required | Description |
17+ | -------------- | -------------- | --------------------------- | --------------------------------------- |
18+ | uid | Buffer(16) | Yes | User ID (FK to accounts.uid) |
19+ | credentialId | Buffer(1-1023) | Yes | WebAuthn credential ID |
20+ | publicKey | Buffer | Yes | COSE-encoded public key |
21+ | signCount | number | Yes | Signature counter (default 0) |
22+ | transports | string[ ] | Yes | Array of transport types (JSON in DB) |
23+ | aaguid | Buffer(16) | Yes | Authenticator AAGUID (may be all-zeros) |
24+ | name | string | Yes | Friendly name (auto-generate required) |
25+ | createdAt | number | Yes | Unix timestamp (ms) |
26+ | lastUsedAt | number \| null | Field: Yes, Value: Nullable | Last auth timestamp (ms) |
27+ | backupEligible | boolean\* | No (default: 0) | Can be backed up |
28+ | backupState | boolean\* | No (default: 0) | Is currently backed up |
29+ | prfEnabled | boolean\* | No (default: 0) | PRF extension enabled |
3030
3131\* Stored as TINYINT(1) in MySQL with DEFAULT 0, converted to boolean by Kysely
3232
@@ -84,27 +84,30 @@ This document provides detailed information about the passkey data model and fie
8484
8585### transports
8686
87- - ** Type** : string | null
88- - ** Storage** : VARCHAR(255)
89- - ** Description** : JSON-encoded array of authenticator transport methods
87+ - ** Type** : string[ ] (JSON array in database)
88+ - ** Required** : Yes
89+ - ** Storage** : JSON NOT NULL
90+ - ** Description** : Array of authenticator transport methods
9091- ** Spec** : [ AuthenticatorTransport enum] ( https://www.w3.org/TR/webauthn-3/#enum-transport )
9192- ** Source** : From [ PublicKeyCredentialDescriptor.transports] ( https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialdescriptor-transports )
9293- ** Validation** : Provided by WebAuthn library (e.g., @simplewebauthn/server ) which validates spec compliance
94+ - ** Storage Pattern** : Store ` [] ` (empty array) when not provided, or JSON array like ` ["internal","hybrid"] `
9395- ** Valid Values** :
9496 - ` "internal" ` - Platform authenticator (Touch ID, Windows Hello)
9597 - ` "usb" ` - USB security key
9698 - ` "nfc" ` - NFC-enabled authenticator
9799 - ` "ble" ` - Bluetooth Low Energy
98100 - ` "hybrid" ` - Cloud-assisted BLE (formerly caBLE), per [ CTAP 2.2] ( https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html )
99101 - ` "smart-card" ` - Smart card authenticator
100- - ** Example** : ` ' ["internal","hybrid"]' ` for iCloud Keychain
102+ - ** Example** : ` ["internal","hybrid"] ` for iCloud Keychain
101103- ** Usage** : Helps UI show appropriate icons and authentication prompts
102- - ** Storage Format ** : JSON array as string for efficient querying
104+ - ** Database Type ** : MySQL JSON for automatic validation and efficient JSON queries
103105
104106### aaguid
105107
106- - ** Type** : Buffer(16) | null
107- - ** Storage** : BINARY(16)
108+ - ** Type** : Buffer(16)
109+ - ** Required** : Yes
110+ - ** Storage** : BINARY(16) NOT NULL
108111- ** Description** : Authenticator Attestation GUID
109112- ** Spec** : [ AAGUID] ( https://www.w3.org/TR/webauthn-3/#aaguid )
110113- ** Source** : From authenticator's [ attestedCredentialData] ( https://www.w3.org/TR/webauthn-3/#attested-credential-data )
@@ -118,34 +121,37 @@ This document provides detailed information about the passkey data model and fie
118121 - Software authenticators (browser built-in passkey managers)
119122 - Privacy-focused hardware keys
120123 - Platform authenticators (depending on policy)
121- - ** Normalization ** : The PasskeyService normalizes all-zeros AAGUID to ` NULL ` before storage
122- - WebAuthn libraries return the raw AAGUID buffer from authenticator data
123- - Service layer converts all -zeros to NULL ( no meaningful identifier)
124- - Only actual AAGUID values that identify specific authenticator models are stored
124+ - ** Storage ** : Store the AAGUID exactly as provided by the authenticator (including all-zeros)
125+ - No normalization needed - all-zeros is a valid value per WebAuthn spec
126+ - All -zeros means "privacy-preserving, no authenticator identifier"
127+ - Simplifies implementation by avoiding special handling
125128
126- ** When to expect meaningful AAGUIDs :**
129+ ** When AAGUIDs identify specific authenticators :**
127130
128131- Hardware security keys (YubiKey, Titan Key, Feitian)
129132- Some platform authenticators (Windows Hello, Touch ID on certain configurations)
130133- Enterprise authenticators
131134
132- - ** Usage** :
133- - Track which authenticator models are in use
134- - Apply vendor-specific quirks if needed
135- - Analytics on authenticator adoption
136- - Security policies (e.g., allow/deny specific authenticator models)
137- - Auto-generate passkey names via FIDO MDS lookup (when not all-zeros)
135+ ** Usage:**
136+
137+ - Track which authenticator models are in use
138+ - Apply vendor-specific quirks if needed
139+ - Analytics on authenticator adoption
140+ - Security policies (e.g., allow/deny specific authenticator models)
141+ - Auto-generate passkey names via FIDO MDS lookup (skip if all-zeros)
138142- ** Example** : YubiKey 5 has AAGUID ` 2fc0579f-8113-47ea-b116-bb5a8db9202a ` , Windows Hello uses ` 08987058-cadc-4b81-b6e1-30de50dcbe96 `
139143- ** Registry** : [ FIDO Alliance Metadata Service] ( https://fidoalliance.org/metadata/ ) provides AAGUID registry
140144
141145## User Management Fields
142146
143147### name
144148
145- - ** Type** : string | null
146- - ** Storage** : VARCHAR(255)
149+ - ** Type** : string
150+ - ** Required** : Yes
151+ - ** Storage** : VARCHAR(255) NOT NULL
147152- ** Description** : User-friendly name for the passkey
148153- ** Note** : FxA-specific field for UX, not part of WebAuthn spec
154+ - ** Important** : Must always be provided during registration (auto-generated or user-provided)
149155- ** Examples** :
150156 - "Touch ID" (auto-generated from platform)
151157 - "YubiKey 5" (auto-generated from AAGUID)
@@ -172,12 +178,19 @@ Generate a descriptive default name using available metadata:
172178 - "iPhone Face ID"
173179 - "YubiKey 5 (Work Laptop)"
174180
181+ ** Database Requirement:**
182+
183+ - The ` name ` field is ** NOT NULL** in the database schema
184+ - Implementation ** MUST** always provide a name during registration
185+ - Never attempt to insert a passkey without a name (will fail DB constraint)
186+
175187** Best Practices:**
176188
177- - Always set a default name on registration (never leave NULL)
178- - Allow users to rename credentials later
189+ - Auto-generate descriptive names using the strategy above
190+ - Allow users to rename credentials after registration
179191- Include date/device info for multiple credentials of same type
180192- Example: "Touch ID (MacBook Pro, Jan 2025)"
193+ - Minimum fallback: Use "Passkey" if all metadata lookups fail
181194
182195### createdAt
183196
@@ -314,15 +327,37 @@ The PRF (Pseudo-Random Function) extension allows authenticators to derive deter
314327
315328### Type Conversion (Kysely ColumnType)
316329
317- Backup flags use ` Generated<ColumnType<boolean, number, number>> ` :
330+ ** Backup flags** use ` Generated<ColumnType<boolean, number, number>> ` :
318331
319332- ** SELECT** : Returns ` boolean ` (true/false) - always present
320333- ** INSERT** : Accepts ` number | undefined ` (0, 1, or omit for default 0) - optional
321334- ** UPDATE** : Accepts ` number | undefined ` (0, 1, or omit to keep current) - optional
322335
323336The ` Generated<...> ` wrapper makes these fields optional on INSERT/UPDATE (matching the database DEFAULT 0), while ensuring they're always present as booleans on SELECT.
324337
325- This provides ergonomic boolean usage in application code while storing efficiently in MySQL.
338+ ** JSON fields** (` transports ` ):
339+
340+ - ** Database** : MySQL JSON type for automatic validation
341+ - ** TypeScript** : ` Json ` type from Kysely (represents JSON values)
342+ - ** INSERT/UPDATE** : Provide as JavaScript value (e.g., ` ['internal', 'hybrid'] ` ) or JSON string
343+ - ** SELECT** : Returns parsed JSON value
344+ - ** Important** : Always provide at least ` [] ` (empty array) - never undefined/null
345+ - ** Example** :
346+
347+ ``` typescript
348+ // Insert
349+ await db .insertInto (' passkeys' ).values ({
350+ transports: [' internal' , ' hybrid' ], // Kysely handles serialization
351+ // ... other fields
352+ });
353+
354+ // Query result
355+ const passkey = await db
356+ .selectFrom (' passkeys' )
357+ .selectAll ()
358+ .executeTakeFirst ();
359+ const transports = passkey .transports ; // Already parsed, e.g., ['internal', 'hybrid']
360+ ```
326361
327362### Indexes
328363
@@ -333,8 +368,8 @@ Note: No additional indexes needed. The number of passkeys per user is constrain
333368
334369### Foreign Keys
335370
336- - ` uid ` references ` accounts(uid) ` with ` ON DELETE CASCADE `
337- - When an account is deleted, all associated passkeys are automatically removed
371+ - ` uid ` references ` accounts(uid) ` (no CASCADE - handled by deleteAccount stored procedure)
372+ - When an account is deleted, passkeys are explicitly deleted via ` deleteAccount_22 ` stored procedure
338373
339374## Implementation Resources
340375
@@ -366,5 +401,5 @@ For generating fallback names when AAGUID is unavailable:
366401
367402## Migration References
368403
369- - ** Forward Migration** : ` packages/db-migrations/databases/fxa/patches/patch-182-183 .sql `
370- - ** Rollback Migration** : ` packages/db-migrations/databases/fxa/patches/patch-183-182 .sql `
404+ - ** Forward Migration** : ` packages/db-migrations/databases/fxa/patches/patch-184-185 .sql `
405+ - ** Rollback Migration** : ` packages/db-migrations/databases/fxa/patches/patch-185-184 .sql `
0 commit comments