From 801dc1d7732c1b5ef4a3dbf80d823396f85c47cb Mon Sep 17 00:00:00 2001 From: Usman Baig Date: Mon, 23 Feb 2026 20:18:18 +0100 Subject: [PATCH] chore: add @simplewebauthn/browser dependency to package.json and package-lock.json for WebAuthn support --- components/settings/ProfileSettings.tsx | 4 ++ lib/api/webauthn.ts | 54 +++++++++++++++++++++++++ package-lock.json | 7 ++++ package.json | 1 + 4 files changed, 66 insertions(+) create mode 100644 lib/api/webauthn.ts diff --git a/components/settings/ProfileSettings.tsx b/components/settings/ProfileSettings.tsx index 6713140..56a91af 100644 --- a/components/settings/ProfileSettings.tsx +++ b/components/settings/ProfileSettings.tsx @@ -6,6 +6,7 @@ import api from '@/lib/api/client' import { deriveAuthKey } from '@/lib/crypto/password' import { deleteAccount, getUserSessions, revokeSession, updateUserPreferences, updateDisplayName } from '@/lib/api/user' import { setup2FA, verify2FA, disable2FA, regenerateRecoveryCodes } from '@/lib/api/2fa' +import { registerPasskey, listPasskeys, deletePasskey } from '@/lib/api/webauthn' export default function ProfileSettings() { const { user, refresh, logout } = useAuth() @@ -46,6 +47,9 @@ export default function ProfileSettings() { onRegenerateRecoveryCodes={regenerateRecoveryCodes} onGetSessions={getUserSessions} onRevokeSession={revokeSession} + onRegisterPasskey={registerPasskey} + onListPasskeys={listPasskeys} + onDeletePasskey={deletePasskey} onUpdatePreferences={updateUserPreferences} deriveAuthKey={deriveAuthKey} refreshUser={refresh} diff --git a/lib/api/webauthn.ts b/lib/api/webauthn.ts new file mode 100644 index 0000000..e3f2fe8 --- /dev/null +++ b/lib/api/webauthn.ts @@ -0,0 +1,54 @@ +/** + * WebAuthn / Passkey API client for settings (list, register, delete). + */ + +import { startRegistration, type PublicKeyCredentialCreationOptionsJSON } from '@simplewebauthn/browser' +import apiRequest from './client' + +export interface BeginRegistrationResponse { + sessionId: string + creationOptions: { + publicKey: Record + mediation?: string + } +} + +export interface PasskeyCredential { + id: string + createdAt: string +} + +export interface ListPasskeysResponse { + credentials: PasskeyCredential[] +} + +export async function registerPasskey(): Promise { + const { sessionId, creationOptions } = await apiRequest( + '/auth/webauthn/register/begin', + { method: 'POST' } + ) + const optionsJSON = creationOptions?.publicKey + if (!optionsJSON) { + throw new Error('Invalid registration options') + } + const response = await startRegistration({ + optionsJSON: optionsJSON as unknown as PublicKeyCredentialCreationOptionsJSON, + }) + await apiRequest<{ message: string }>('/auth/webauthn/register/finish', { + method: 'POST', + body: JSON.stringify({ sessionId, response }), + }) +} + +export async function listPasskeys(): Promise { + return apiRequest('/auth/webauthn/credentials', { + method: 'GET', + }) +} + +export async function deletePasskey(credentialId: string): Promise { + return apiRequest( + `/auth/webauthn/credentials/${encodeURIComponent(credentialId)}`, + { method: 'DELETE' } + ) +} diff --git a/package-lock.json b/package-lock.json index b1fc630..3ca446f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@ciphera-net/ui": "^0.0.64", "@ducanh2912/next-pwa": "^10.2.9", "@radix-ui/react-icons": "^1.3.0", + "@simplewebauthn/browser": "^13.2.2", "@stripe/react-stripe-js": "^5.6.0", "@stripe/stripe-js": "^8.7.0", "axios": "^1.13.2", @@ -2717,6 +2718,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@simplewebauthn/browser": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@simplewebauthn/browser/-/browser-13.2.2.tgz", + "integrity": "sha512-FNW1oLQpTJyqG5kkDg5ZsotvWgmBaC6jCHR7Ej0qUNep36Wl9tj2eZu7J5rP+uhXgHaLk+QQ3lqcw2vS5MX1IA==", + "license": "MIT" + }, "node_modules/@stripe/react-stripe-js": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/@stripe/react-stripe-js/-/react-stripe-js-5.6.0.tgz", diff --git a/package.json b/package.json index 14ec466..f8d66ec 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@ciphera-net/ui": "^0.0.64", "@ducanh2912/next-pwa": "^10.2.9", "@radix-ui/react-icons": "^1.3.0", + "@simplewebauthn/browser": "^13.2.2", "@stripe/react-stripe-js": "^5.6.0", "@stripe/stripe-js": "^8.7.0", "axios": "^1.13.2",