85dda211aed3
add PasskeyData sub-object + entry.passkey field to VaultEntry, hasPasskey/isPasskeyOnly helpers, keep flat fields as deprecated for migration
- time
- 2026-04-28 21:59 UTC · 6 months ago
- machine
- koh2
- parent
- a86e59d61c04
↓ peach-browser-85dda211.tar.gzatlas at this save
files changed1 file
src/shared/types.ts+62 −4src/shared/types.ts+62 −4
@@ -24,9 +24,46 @@2424 2525 export type CardNetwork = "visa" | "mastercard" | "amex" | "discover" | "unknown";2626 27+/**28+ * Passkey credential data attached to a VaultEntry. New in v2.11.0 — passkeys29+ * are no longer a separate entry type; instead they hang off any login entry.30+ * A passkey-only entry is a `type:"login"` entry with no username/password31+ * but a `passkey` sub-object set. A login that ALSO has a passkey is just a32+ * normal login with `passkey` populated.33+ *34+ * privateKeyJwk is present only on Chrome-created passkeys, where Peach is35+ * the authenticator. Firefox-created passkeys delegate to the platform36+ * authenticator, so the field is absent — `browser` distinguishes the two.37+ */38+export interface PasskeyData {39+ rpId: string;40+ rpName: string;41+ credentialId: string;42+ publicKeyCose: string;43+ privateKeyJwk?: JsonWebKey;44+ userHandle: string;45+ userName: string;46+ userDisplayName: string;47+ algorithm: -7 | -8;48+ signCount: number;49+ aaguid: string;50+ backupEligible: boolean;51+ backupState: boolean;52+ lastUsedAt?: number;53+ browser?: "chrome" | "firefox";54+ transports?: string[];55+ /** When the credential was first registered with Peach. */56+ createdAt: number;57+}58+2759 export interface VaultEntry {2860 id: string; // crypto.randomUUID()29- type?: "login" | "note" | "passkey" | "card"; // defaults to "login" for existing entries61+ /**62+ * Entry type. As of v2.11.0 passkeys are stored on `type:"login"` entries63+ * via the `passkey` sub-object — `"passkey"` here is a legacy value still64+ * accepted on read but rewritten to `"login"` on next vault save.65+ */66+ type?: "login" | "note" | "passkey" | "card";3067 title: string;3168 username?: string; // only for login entries3269 password?: string; // only for login entries@@ -52,6 +89,12 @@5289 modifiedAt: number;5390 deletedAt?: number;5491 tags?: string[];92+ /** Passkey credential attached to this entry, if any. */93+ passkey?: PasskeyData;94+ // ── Legacy passkey flat fields (pre-v2.11.0). Populated by vault read95+ // migration so that the old import/sanitize paths keep typing; new code96+ // should always read `entry.passkey?.*` instead. The migration drops these97+ // on next vault save. Do not introduce new readers of these fields.5598 rpId?: string;5699 rpName?: string;57100 credentialId?: string;@@ -66,9 +109,6 @@66109 backupEligible?: boolean;67110 backupState?: boolean;68111 lastUsedAt?: number;69- // Passkeys are not portable between browsers: a Chrome passkey holds its70- // private key in Peach's vault, while a Firefox passkey delegates to the71- // platform authenticator. Tag each entry so the UI can warn cross-browser.72112 browser?: "chrome" | "firefox";73113 transports?: string[];74114 // Card entry fields@@ -80,6 +120,24 @@80120 cardNickname?: string;81121 network?: CardNetwork;82122 last4?: string;123+}124+125+/**126+ * Returns true when an entry has a passkey credential attached. After v2.11.0127+ * migration this is the canonical check; pre-migration entries with a flat128+ * `rpId`/`credentialId` shape get rewritten by the vault read pass before129+ * any consumer sees them.130+ */131+export function hasPasskey(entry: VaultEntry): boolean {132+ return entry.passkey !== undefined && entry.passkey.credentialId.length > 0;133+}134+135+/**136+ * Returns true when an entry is "passkey-only" — has a passkey but no137+ * username or password. Such entries render with login fields hidden.138+ */139+export function isPasskeyOnly(entry: VaultEntry): boolean {140+ return hasPasskey(entry) && !entry.username && !entry.password;83141 }84142 85143 export interface LegacyVaultEntry {