UNPKG

expo-passkey

Version:

Passkey authentication for Expo apps with Better Auth integration

107 lines 4.44 kB
/** * @file Revoke passkey endpoint * @description Implementation of the endpoint to revoke a WebAuthn passkey */ import { createAuthEndpoint } from "better-auth/api"; import { APIError } from "better-call"; import { ERROR_CODES, ERROR_MESSAGES } from "../../types/errors"; import { revokePasskeySchema } from "../utils/schema"; /** * Create endpoint to revoke a passkey */ export const createRevokeEndpoint = (options) => { const { logger, schemaConfig } = options; return createAuthEndpoint("/expo-passkey/revoke", { method: "POST", body: revokePasskeySchema, metadata: { openapi: { description: "Revoke a registered WebAuthn passkey", tags: ["Authentication"], responses: { 200: { description: "Passkey successfully revoked", content: { "application/json": { schema: { type: "object", properties: { success: { type: "boolean" }, }, }, }, }, }, 404: { description: "Passkey not found", content: { "application/json": { schema: { type: "object", properties: { error: { type: "object", properties: { code: { type: "string" }, message: { type: "string" }, }, }, }, }, }, }, }, }, }, }, }, async (ctx) => { const { userId, credentialId, reason } = ctx.body; try { logger.debug("Revoking passkey", { userId, credentialId }); // Find the active credential for the provided credential ID and user ID const credential = await ctx.context.adapter.findOne({ model: schemaConfig.authPasskeyModel, where: [ { field: "credentialId", operator: "eq", value: credentialId }, { field: "userId", operator: "eq", value: userId }, { field: "status", operator: "eq", value: "active" }, ], }); if (!credential) { logger.warn("Revoke failed: Passkey not found", { credentialId }); throw new APIError("NOT_FOUND", { code: ERROR_CODES.SERVER.CREDENTIAL_NOT_FOUND, message: ERROR_MESSAGES[ERROR_CODES.SERVER.CREDENTIAL_NOT_FOUND], }); } const now = new Date().toISOString(); // Update the credential to revoked status await ctx.context.adapter.update({ model: schemaConfig.authPasskeyModel, where: [{ field: "id", operator: "eq", value: credential.id }], update: { status: "revoked", revokedAt: now, revokedReason: reason || "user_initiated", updatedAt: now, }, }); logger.info("Passkey revoked successfully", { userId, credentialId, reason: reason || "user_initiated", }); return ctx.json({ success: true }); } catch (error) { logger.error("Failed to revoke passkey", error); if (error instanceof APIError) throw error; throw new APIError("BAD_REQUEST", { code: ERROR_CODES.SERVER.REVOCATION_FAILED, message: ERROR_MESSAGES[ERROR_CODES.SERVER.REVOCATION_FAILED], }); } }); }; //# sourceMappingURL=revoke.js.map