diff --git a/server/db/pg/schema.ts b/server/db/pg/schema.ts index 05389d42..045cba73 100644 --- a/server/db/pg/schema.ts +++ b/server/db/pg/schema.ts @@ -491,10 +491,24 @@ export const idpOrg = pgTable("idpOrg", { orgMapping: varchar("orgMapping") }); +export const securityKeys = pgTable("webauthnCredentials", { + credentialId: varchar("credentialId").primaryKey(), + userId: varchar("userId").notNull().references(() => users.userId, { + onDelete: "cascade" + }), + publicKey: varchar("publicKey").notNull(), + signCount: integer("signCount").notNull(), + transports: varchar("transports"), + name: varchar("name"), + lastUsed: varchar("lastUsed").notNull(), + dateCreated: varchar("dateCreated").notNull(), + securityKeyName: varchar("securityKeyName") +}); + export const webauthnChallenge = pgTable("webauthnChallenge", { sessionId: varchar("sessionId").primaryKey(), challenge: varchar("challenge").notNull(), - passkeyName: varchar("passkeyName"), + securityKeyName: varchar("securityKeyName"), userId: varchar("userId").references(() => users.userId, { onDelete: "cascade" }), diff --git a/server/db/sqlite/migrate.ts b/server/db/sqlite/migrate.ts index 10cec1a6..55399ea6 100644 --- a/server/db/sqlite/migrate.ts +++ b/server/db/sqlite/migrate.ts @@ -7,12 +7,37 @@ import type { Database as BetterSqlite3Database } from "better-sqlite3"; const migrationsFolder = path.join("server/migrations"); +const dropAllTables = (sqlite: BetterSqlite3Database) => { + console.log("Dropping all existing tables..."); + + // Disable foreign key checks + sqlite.pragma('foreign_keys = OFF'); + + // Get all tables + const tables = sqlite.prepare(` + SELECT name FROM sqlite_master + WHERE type='table' + AND name NOT LIKE 'sqlite_%' + `).all() as { name: string }[]; + + // Drop each table + for (const table of tables) { + console.log(`Dropping table: ${table.name}`); + sqlite.prepare(`DROP TABLE IF EXISTS "${table.name}"`).run(); + } + + // Re-enable foreign key checks + sqlite.pragma('foreign_keys = ON'); +}; + const runMigrations = async () => { console.log("Running migrations..."); try { // Initialize the database file with a valid SQLite header const sqlite = new Database(location) as BetterSqlite3Database; - sqlite.pragma('foreign_keys = ON'); + + // Drop all existing tables first + dropAllTables(sqlite); // Run the migrations migrate(db as any, { diff --git a/server/db/sqlite/schema.ts b/server/db/sqlite/schema.ts index 1a6dadef..1425385f 100644 --- a/server/db/sqlite/schema.ts +++ b/server/db/sqlite/schema.ts @@ -135,7 +135,7 @@ export const users = sqliteTable("user", { .default(false) }); -export const passkeys = sqliteTable("webauthnCredentials", { +export const securityKeys = sqliteTable("webauthnCredentials", { credentialId: text("credentialId").primaryKey(), userId: text("userId").notNull().references(() => users.userId, { onDelete: "cascade" @@ -151,7 +151,7 @@ export const passkeys = sqliteTable("webauthnCredentials", { export const webauthnChallenge = sqliteTable("webauthnChallenge", { sessionId: text("sessionId").primaryKey(), challenge: text("challenge").notNull(), - passkeyName: text("passkeyName"), + securityKeyName: text("securityKeyName"), userId: text("userId").references(() => users.userId, { onDelete: "cascade" }), diff --git a/server/routers/auth/index.ts b/server/routers/auth/index.ts index 4c7014a4..cc8fd630 100644 --- a/server/routers/auth/index.ts +++ b/server/routers/auth/index.ts @@ -6,10 +6,10 @@ export * from "./requestTotpSecret"; export * from "./disable2fa"; export * from "./verifyEmail"; export * from "./requestEmailVerificationCode"; -export * from "./changePassword"; -export * from "./requestPasswordReset"; export * from "./resetPassword"; -export * from "./checkResourceSession"; +export * from "./requestPasswordReset"; export * from "./setServerAdmin"; export * from "./initialSetupComplete"; -export * from "./passkey"; +export * from "./changePassword"; +export * from "./checkResourceSession"; +export * from "./securityKey"; diff --git a/server/routers/auth/login.ts b/server/routers/auth/login.ts index 72040a03..2dbbde1c 100644 --- a/server/routers/auth/login.ts +++ b/server/routers/auth/login.ts @@ -4,7 +4,7 @@ import { serializeSessionCookie } from "@server/auth/sessions/app"; import { db } from "@server/db"; -import { users, passkeys } from "@server/db"; +import { users, securityKeys } from "@server/db"; import HttpCode from "@server/types/HttpCode"; import response from "@server/lib/response"; import { eq, and } from "drizzle-orm"; @@ -35,6 +35,7 @@ export type LoginBody = z.infer; export type LoginResponse = { codeRequested?: boolean; emailVerificationRequired?: boolean; + useSecurityKey?: boolean; }; export const dynamic = "force-dynamic"; @@ -91,15 +92,15 @@ export async function login( const existingUser = existingUserRes[0]; - // Check if user has passkeys registered - const userPasskeys = await db + // Check if user has security keys registered + const userSecurityKeys = await db .select() - .from(passkeys) - .where(eq(passkeys.userId, existingUser.userId)); + .from(securityKeys) + .where(eq(securityKeys.userId, existingUser.userId)); - if (userPasskeys.length > 0) { - return response<{ usePasskey: boolean }>(res, { - data: { usePasskey: true }, + if (userSecurityKeys.length > 0) { + return response<{ useSecurityKey: boolean }>(res, { + data: { useSecurityKey: true }, success: true, error: false, message: "Please use your security key to sign in", diff --git a/server/routers/auth/passkey.ts b/server/routers/auth/securityKey.ts similarity index 83% rename from server/routers/auth/passkey.ts rename to server/routers/auth/securityKey.ts index 268f2144..c5ee48e6 100644 --- a/server/routers/auth/passkey.ts +++ b/server/routers/auth/securityKey.ts @@ -4,7 +4,7 @@ import HttpCode from "@server/types/HttpCode"; import { fromError } from "zod-validation-error"; import { z } from "zod"; import { db } from "@server/db"; -import { User, passkeys, users, webauthnChallenge } from "@server/db"; +import { User, securityKeys, users, webauthnChallenge } from "@server/db"; import { eq, and, lt } from "drizzle-orm"; import { response } from "@server/lib"; import logger from "@server/logger"; @@ -55,14 +55,14 @@ setInterval(async () => { await db .delete(webauthnChallenge) .where(lt(webauthnChallenge.expiresAt, now)); - logger.debug("Cleaned up expired passkey challenges"); + logger.debug("Cleaned up expired security key challenges"); } catch (error) { - logger.error("Failed to clean up expired passkey challenges", error); + logger.error("Failed to clean up expired security key challenges", error); } }, 5 * 60 * 1000); // Helper functions for challenge management -async function storeChallenge(sessionId: string, challenge: string, passkeyName?: string, userId?: string) { +async function storeChallenge(sessionId: string, challenge: string, securityKeyName?: string, userId?: string) { const expiresAt = Date.now() + (5 * 60 * 1000); // 5 minutes // Delete any existing challenge for this session @@ -72,7 +72,7 @@ async function storeChallenge(sessionId: string, challenge: string, passkeyName? await db.insert(webauthnChallenge).values({ sessionId, challenge, - passkeyName, + securityKeyName, userId, expiresAt }); @@ -102,7 +102,7 @@ async function clearChallenge(sessionId: string) { await db.delete(webauthnChallenge).where(eq(webauthnChallenge.sessionId, sessionId)); } -export const registerPasskeyBody = z.object({ +export const registerSecurityKeyBody = z.object({ name: z.string().min(1), password: z.string().min(1) }).strict(); @@ -119,7 +119,7 @@ export const verifyAuthenticationBody = z.object({ credential: z.any() }).strict(); -export const deletePasskeyBody = z.object({ +export const deleteSecurityKeyBody = z.object({ password: z.string().min(1) }).strict(); @@ -128,7 +128,7 @@ export async function startRegistration( res: Response, next: NextFunction ): Promise { - const parsedBody = registerPasskeyBody.safeParse(req.body); + const parsedBody = registerSecurityKeyBody.safeParse(req.body); if (!parsedBody.success) { return next( @@ -142,12 +142,12 @@ export async function startRegistration( const { name, password } = parsedBody.data; const user = req.user as User; - // Only allow internal users to use passkeys + // Only allow internal users to use security keys if (user.type !== UserType.Internal) { return next( createHttpError( HttpCode.BAD_REQUEST, - "Passkeys are only available for internal users" + "Security keys are only available for internal users" ) ); } @@ -170,13 +170,13 @@ export async function startRegistration( }); } - // Get existing passkeys for user - const existingPasskeys = await db + // Get existing security keys for user + const existingSecurityKeys = await db .select() - .from(passkeys) - .where(eq(passkeys.userId, user.userId)); + .from(securityKeys) + .where(eq(securityKeys.userId, user.userId)); - const excludeCredentials = existingPasskeys.map(key => ({ + const excludeCredentials = existingSecurityKeys.map(key => ({ id: Buffer.from(key.credentialId, 'base64').toString('base64url'), type: 'public-key' as const, transports: key.transports ? JSON.parse(key.transports) as AuthenticatorTransport[] : undefined @@ -237,12 +237,12 @@ export async function verifyRegistration( const { credential } = parsedBody.data; const user = req.user as User; - // Only allow internal users to use passkeys + // Only allow internal users to use security keys if (user.type !== UserType.Internal) { return next( createHttpError( HttpCode.BAD_REQUEST, - "Passkeys are only available for internal users" + "Security keys are only available for internal users" ) ); } @@ -279,14 +279,14 @@ export async function verifyRegistration( ); } - // Store the passkey in the database - await db.insert(passkeys).values({ + // Store the security key in the database + await db.insert(securityKeys).values({ credentialId: Buffer.from(registrationInfo.credentialID).toString('base64'), userId: user.userId, publicKey: Buffer.from(registrationInfo.credentialPublicKey).toString('base64'), signCount: registrationInfo.counter || 0, transports: credential.response.transports ? JSON.stringify(credential.response.transports) : null, - name: challengeData.passkeyName, + name: challengeData.securityKeyName, lastUsed: new Date().toISOString(), dateCreated: new Date().toISOString() }); @@ -298,7 +298,7 @@ export async function verifyRegistration( data: null, success: true, error: false, - message: "Passkey registered successfully", + message: "Security key registered successfully", status: HttpCode.OK }); } catch (error) { @@ -312,34 +312,34 @@ export async function verifyRegistration( } } -export async function listPasskeys( +export async function listSecurityKeys( req: Request, res: Response, next: NextFunction ): Promise { const user = req.user as User; - // Only allow internal users to use passkeys + // Only allow internal users to use security keys if (user.type !== UserType.Internal) { return next( createHttpError( HttpCode.BAD_REQUEST, - "Passkeys are only available for internal users" + "Security keys are only available for internal users" ) ); } try { - const userPasskeys = await db + const userSecurityKeys = await db .select() - .from(passkeys) - .where(eq(passkeys.userId, user.userId)); + .from(securityKeys) + .where(eq(securityKeys.userId, user.userId)); - return response(res, { - data: userPasskeys, + return response(res, { + data: userSecurityKeys, success: true, error: false, - message: "Passkeys retrieved successfully", + message: "Security keys retrieved successfully", status: HttpCode.OK }); } catch (error) { @@ -347,13 +347,13 @@ export async function listPasskeys( return next( createHttpError( HttpCode.INTERNAL_SERVER_ERROR, - "Failed to retrieve passkeys" + "Failed to retrieve security keys" ) ); } } -export async function deletePasskey( +export async function deleteSecurityKey( req: Request, res: Response, next: NextFunction @@ -362,7 +362,7 @@ export async function deletePasskey( const credentialId = decodeURIComponent(encodedCredentialId); const user = req.user as User; - const parsedBody = deletePasskeyBody.safeParse(req.body); + const parsedBody = deleteSecurityKeyBody.safeParse(req.body); if (!parsedBody.success) { return next( @@ -375,12 +375,12 @@ export async function deletePasskey( const { password } = parsedBody.data; - // Only allow internal users to use passkeys + // Only allow internal users to use security keys if (user.type !== UserType.Internal) { return next( createHttpError( HttpCode.BAD_REQUEST, - "Passkeys are only available for internal users" + "Security keys are only available for internal users" ) ); } @@ -404,17 +404,17 @@ export async function deletePasskey( } await db - .delete(passkeys) + .delete(securityKeys) .where(and( - eq(passkeys.credentialId, credentialId), - eq(passkeys.userId, user.userId) + eq(securityKeys.credentialId, credentialId), + eq(securityKeys.userId, user.userId) )); return response(res, { data: null, success: true, error: false, - message: "Passkey deleted successfully", + message: "Security key deleted successfully", status: HttpCode.OK }); } catch (error) { @@ -422,7 +422,7 @@ export async function deletePasskey( return next( createHttpError( HttpCode.INTERNAL_SERVER_ERROR, - "Failed to delete passkey" + "Failed to delete security key" ) ); } @@ -454,7 +454,7 @@ export async function startAuthentication( }> = []; let userId; - // If email is provided, get passkeys for that specific user + // If email is provided, get security keys for that specific user if (email) { const [user] = await db .select() @@ -473,27 +473,27 @@ export async function startAuthentication( userId = user.userId; - const userPasskeys = await db + const userSecurityKeys = await db .select() - .from(passkeys) - .where(eq(passkeys.userId, user.userId)); + .from(securityKeys) + .where(eq(securityKeys.userId, user.userId)); - if (userPasskeys.length === 0) { + if (userSecurityKeys.length === 0) { return next( createHttpError( HttpCode.BAD_REQUEST, - "No passkeys registered for this user" + "No security keys registered for this user" ) ); } - allowCredentials = userPasskeys.map(key => ({ + allowCredentials = userSecurityKeys.map(key => ({ id: Buffer.from(key.credentialId, 'base64'), type: 'public-key' as const, transports: key.transports ? JSON.parse(key.transports) as AuthenticatorTransport[] : undefined })); } else { - // If no email provided, allow any passkey (for resident key authentication) + // If no email provided, allow any security key (for resident key authentication) allowCredentials = []; } @@ -570,15 +570,15 @@ export async function verifyAuthentication( ); } - // Find the passkey in database + // Find the security key in database const credentialId = Buffer.from(credential.id, 'base64').toString('base64'); - const [passkey] = await db + const [securityKey] = await db .select() - .from(passkeys) - .where(eq(passkeys.credentialId, credentialId)) + .from(securityKeys) + .where(eq(securityKeys.credentialId, credentialId)) .limit(1); - if (!passkey) { + if (!securityKey) { return next( createHttpError( HttpCode.BAD_REQUEST, @@ -591,14 +591,14 @@ export async function verifyAuthentication( const [user] = await db .select() .from(users) - .where(eq(users.userId, passkey.userId)) + .where(eq(users.userId, securityKey.userId)) .limit(1); if (!user || user.type !== UserType.Internal) { return next( createHttpError( HttpCode.BAD_REQUEST, - "User not found or not authorized for passkey authentication" + "User not found or not authorized for security key authentication" ) ); } @@ -609,10 +609,10 @@ export async function verifyAuthentication( expectedOrigin: origin, expectedRPID: rpID, authenticator: { - credentialID: Buffer.from(passkey.credentialId, 'base64'), - credentialPublicKey: Buffer.from(passkey.publicKey, 'base64'), - counter: passkey.signCount, - transports: passkey.transports ? JSON.parse(passkey.transports) as AuthenticatorTransport[] : undefined + credentialID: Buffer.from(securityKey.credentialId, 'base64'), + credentialPublicKey: Buffer.from(securityKey.publicKey, 'base64'), + counter: securityKey.signCount, + transports: securityKey.transports ? JSON.parse(securityKey.transports) as AuthenticatorTransport[] : undefined }, requireUserVerification: false }); @@ -630,12 +630,12 @@ export async function verifyAuthentication( // Update sign count await db - .update(passkeys) + .update(securityKeys) .set({ signCount: authenticationInfo.newCounter, lastUsed: new Date().toISOString() }) - .where(eq(passkeys.credentialId, credentialId)); + .where(eq(securityKeys.credentialId, credentialId)); // Create session for the user const { createSession, generateSessionToken, serializeSessionCookie } = await import("@server/auth/sessions/app"); diff --git a/server/routers/external.ts b/server/routers/external.ts index 1eebb531..c3c2e763 100644 --- a/server/routers/external.ts +++ b/server/routers/external.ts @@ -789,35 +789,35 @@ authRouter.post("/idp/:idpId/oidc/validate-callback", idp.validateOidcCallback); authRouter.put("/set-server-admin", auth.setServerAdmin); authRouter.get("/initial-setup-complete", auth.initialSetupComplete); -// Passkey routes +// Security Key routes authRouter.post( - "/passkey/register/start", + "/security-key/register/start", rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes - max: 5, // Allow 5 passkey registrations per 15 minutes per IP - keyGenerator: (req) => `passkeyRegister:${req.ip}:${req.user?.userId}`, + max: 5, // Allow 5 security key registrations per 15 minutes per IP + keyGenerator: (req) => `securityKeyRegister:${req.ip}:${req.user?.userId}`, handler: (req, res, next) => { - const message = `You can only register ${5} passkeys every ${15} minutes. Please try again later.`; + const message = `You can only register ${5} security keys every ${15} minutes. Please try again later.`; return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message)); } }), verifySessionUserMiddleware, auth.startRegistration ); -authRouter.post("/passkey/register/verify", verifySessionUserMiddleware, auth.verifyRegistration); +authRouter.post("/security-key/register/verify", verifySessionUserMiddleware, auth.verifyRegistration); authRouter.post( - "/passkey/authenticate/start", + "/security-key/authenticate/start", rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 10, // Allow 10 authentication attempts per 15 minutes per IP - keyGenerator: (req) => `passkeyAuth:${req.ip}`, + keyGenerator: (req) => `securityKeyAuth:${req.ip}`, handler: (req, res, next) => { - const message = `You can only attempt passkey authentication ${10} times every ${15} minutes. Please try again later.`; + const message = `You can only attempt security key authentication ${10} times every ${15} minutes. Please try again later.`; return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message)); } }), auth.startAuthentication ); -authRouter.post("/passkey/authenticate/verify", auth.verifyAuthentication); -authRouter.get("/passkey/list", verifySessionUserMiddleware, auth.listPasskeys); -authRouter.delete("/passkey/:credentialId", verifySessionUserMiddleware, auth.deletePasskey); +authRouter.post("/security-key/authenticate/verify", auth.verifyAuthentication); +authRouter.get("/security-key/list", verifySessionUserMiddleware, auth.listSecurityKeys); +authRouter.delete("/security-key/:credentialId", verifySessionUserMiddleware, auth.deleteSecurityKey); diff --git a/server/setup/scriptsSqlite/1.4.0.ts b/server/setup/scriptsSqlite/1.4.0.ts index e961b287..3885b4bf 100644 --- a/server/setup/scriptsSqlite/1.4.0.ts +++ b/server/setup/scriptsSqlite/1.4.0.ts @@ -8,7 +8,7 @@ export default async function migration() { try { db.transaction((trx) => { - trx.run(sql`CREATE TABLE 'passkey' ( + trx.run(sql`CREATE TABLE 'securityKey' ( 'credentialId' text PRIMARY KEY NOT NULL, 'userId' text NOT NULL, 'publicKey' text NOT NULL, diff --git a/server/setup/scriptsSqlite/1.7.0.ts b/server/setup/scriptsSqlite/1.7.0.ts index 153d3724..59166827 100644 --- a/server/setup/scriptsSqlite/1.7.0.ts +++ b/server/setup/scriptsSqlite/1.7.0.ts @@ -14,7 +14,7 @@ export default async function migration() { db.pragma("foreign_keys = OFF"); db.transaction(() => { db.exec(` - CREATE TABLE IF NOT EXISTS passkey ( + CREATE TABLE IF NOT EXISTS securityKey ( credentialId TEXT PRIMARY KEY, userId TEXT NOT NULL, publicKey TEXT NOT NULL, @@ -28,9 +28,9 @@ export default async function migration() { `); })(); // executes the transaction immediately db.pragma("foreign_keys = ON"); - console.log(`Created passkey table`); + console.log(`Created securityKey table`); } catch (e) { - console.error("Unable to create passkey table"); + console.error("Unable to create securityKey table"); console.error(e); throw e; } diff --git a/server/setup/scriptsSqlite/1.8.0.ts b/server/setup/scriptsSqlite/1.8.0.ts index 19ff4052..7777d50e 100644 --- a/server/setup/scriptsSqlite/1.8.0.ts +++ b/server/setup/scriptsSqlite/1.8.0.ts @@ -14,22 +14,22 @@ export default async function migration() { db.pragma("foreign_keys = OFF"); db.transaction(() => { db.exec(` - CREATE TABLE IF NOT EXISTS passkeyChallenge ( + CREATE TABLE IF NOT EXISTS securityKeyChallenge ( sessionId TEXT PRIMARY KEY, challenge TEXT NOT NULL, - passkeyName TEXT, + securityKeyName TEXT, userId TEXT, expiresAt INTEGER NOT NULL, FOREIGN KEY (userId) REFERENCES user(id) ON DELETE CASCADE ); - CREATE INDEX IF NOT EXISTS idx_passkeyChallenge_expiresAt ON passkeyChallenge(expiresAt); + CREATE INDEX IF NOT EXISTS idx_securityKeyChallenge_expiresAt ON securityKeyChallenge(expiresAt); `); })(); // executes the transaction immediately db.pragma("foreign_keys = ON"); - console.log(`Created passkeyChallenge table`); + console.log(`Created securityKeyChallenge table`); } catch (e) { - console.error("Unable to create passkeyChallenge table"); + console.error("Unable to create securityKeyChallenge table"); console.error(e); throw e; } diff --git a/server/setup/scriptsSqlite/1.8.1.ts b/server/setup/scriptsSqlite/1.8.1.ts index 473a354c..560b314a 100644 --- a/server/setup/scriptsSqlite/1.8.1.ts +++ b/server/setup/scriptsSqlite/1.8.1.ts @@ -6,21 +6,21 @@ export default async function migrate() { // Rename the table await db.run(` - ALTER TABLE passkeyChallenge RENAME TO webauthnChallenge; + ALTER TABLE securityKeyChallenge RENAME TO webauthnChallenge; `); console.log("Successfully renamed table"); // Rename the index await db.run(` - DROP INDEX IF EXISTS idx_passkeyChallenge_expiresAt; + DROP INDEX IF EXISTS idx_securityKeyChallenge_expiresAt; CREATE INDEX IF NOT EXISTS idx_webauthnChallenge_expiresAt ON webauthnChallenge(expiresAt); `); console.log("Successfully updated index"); - console.log(`Renamed passkeyChallenge table to webauthnChallenge`); + console.log(`Renamed securityKeyChallenge table to webauthnChallenge`); return true; } catch (error: any) { - console.error("Unable to rename passkeyChallenge table:", error); + console.error("Unable to rename securityKeyChallenge table:", error); console.error("Error details:", error.message); return false; } diff --git a/src/components/LoginForm.tsx b/src/components/LoginForm.tsx index ac435043..e14315eb 100644 --- a/src/components/LoginForm.tsx +++ b/src/components/LoginForm.tsx @@ -4,8 +4,8 @@ import { useState } from "react"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import * as z from "zod"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; +import { Button } from "@app/components/ui/button"; +import { Input } from "@app/components/ui/input"; import { Form, FormControl, @@ -13,15 +13,15 @@ import { FormItem, FormLabel, FormMessage -} from "@/components/ui/form"; +} from "@app/components/ui/form"; import { Card, CardContent, CardDescription, CardHeader, CardTitle -} from "@/components/ui/card"; -import { Alert, AlertDescription } from "@/components/ui/alert"; +} from "@app/components/ui/card"; +import { Alert, AlertDescription } from "@app/components/ui/alert"; import { LoginResponse } from "@server/routers/auth"; import { useRouter } from "next/navigation"; import { AxiosResponse } from "axios"; @@ -120,8 +120,8 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { setError(null); const data = res.data.data; - if (data?.usePasskey) { - await initiateSecurityKeyAuth(); + if (data?.useSecurityKey) { + setShowSecurityKeyPrompt(true); return; } @@ -197,7 +197,7 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { const email = form.getValues().email; // Start WebAuthn authentication - const startRes = await api.post("/auth/passkey/authenticate/start", { + const startRes = await api.post("/auth/security-key/authenticate/start", { email: email || undefined }); @@ -216,7 +216,7 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { // Verify authentication const verifyRes = await api.post( - "/auth/passkey/authenticate/verify", + "/auth/security-key/authenticate/verify", { credential }, { headers: { @@ -355,9 +355,9 @@ export default function LoginForm({ redirect, onLogin, idps }: LoginFormProps) { pattern={ REGEXP_ONLY_DIGITS_AND_CHARS } - onChange={(e) => { - field.onChange(e); - if (e.length === 6) { + onChange={(value: string) => { + field.onChange(value); + if (value.length === 6) { mfaForm.handleSubmit(onSubmit)(); } }} diff --git a/src/components/SecurityKeyForm.tsx b/src/components/SecurityKeyForm.tsx index cc90f64b..a83165bc 100644 --- a/src/components/SecurityKeyForm.tsx +++ b/src/components/SecurityKeyForm.tsx @@ -108,7 +108,7 @@ export default function SecurityKeyForm({ open, setOpen }: SecurityKeyFormProps) const loadSecurityKeys = async () => { try { - const response = await api.get("/auth/passkey/list"); + const response = await api.get("/auth/security-key/list"); setSecurityKeys(response.data.data); } catch (error) { toast({ @@ -132,7 +132,7 @@ export default function SecurityKeyForm({ open, setOpen }: SecurityKeyFormProps) } setIsRegistering(true); - const startRes = await api.post("/auth/passkey/register/start", { + const startRes = await api.post("/auth/security-key/register/start", { name: values.name, password: values.password, }); @@ -152,7 +152,7 @@ export default function SecurityKeyForm({ open, setOpen }: SecurityKeyFormProps) try { const credential = await startRegistration(options); - await api.post("/auth/passkey/register/verify", { + await api.post("/auth/security-key/register/verify", { credential, }); @@ -217,7 +217,7 @@ export default function SecurityKeyForm({ open, setOpen }: SecurityKeyFormProps) try { const encodedCredentialId = encodeURIComponent(selectedSecurityKey.credentialId); - await api.delete(`/auth/passkey/${encodedCredentialId}`, { + await api.delete(`/auth/security-key/${encodedCredentialId}`, { data: { password: values.password, }