zerobyte/app/client/modules/auth/components/passkey-sign-in-button.tsx
Nico a488bbc754
fix: block login for 2fa users with un-verified passkeys (#934)
* fix: block login for 2fa users with un-verified passkeys

* refactor(passkey): show proper login error

* refactor: show passkey generic error on all failures
2026-06-02 19:48:40 +02:00

64 lines
1.6 KiB
TypeScript

import { useMutation } from "@tanstack/react-query";
import { useNavigate } from "@tanstack/react-router";
import { Fingerprint } from "lucide-react";
import { Button } from "~/client/components/ui/button";
import { authClient } from "~/client/lib/auth-client";
import { logger } from "~/client/lib/logger";
import { LOGIN_ERROR_CODES, PASSKEY_LOGIN_FAILED_ERROR, type LoginErrorCode } from "~/lib/sso-errors";
type PasskeySignInButtonProps = {
onSignIn: () => Promise<void>;
};
type PasskeySignInError = {
code?: string;
message?: string;
status?: number;
statusText?: string;
};
const LOGIN_ERROR_CODE_SET = new Set<string>(LOGIN_ERROR_CODES);
function getPasskeyLoginErrorCode(code: string | undefined): LoginErrorCode {
if (code && LOGIN_ERROR_CODE_SET.has(code)) {
return code as LoginErrorCode;
}
return PASSKEY_LOGIN_FAILED_ERROR;
}
export function PasskeySignInButton({ onSignIn }: PasskeySignInButtonProps) {
const navigate = useNavigate();
const passkeyLoginMutation = useMutation<void, PasskeySignInError>({
mutationFn: async () => {
const { error } = await authClient.signIn.passkey();
if (error) throw error;
await onSignIn();
},
onError: (error) => {
logger.error(error);
void navigate({
to: "/login",
search: {
error: getPasskeyLoginErrorCode(error.code),
},
});
},
});
return (
<Button
type="button"
variant="outline"
className="w-full"
loading={passkeyLoginMutation.isPending}
disabled={passkeyLoginMutation.isPending}
onClick={() => passkeyLoginMutation.mutate()}
>
<Fingerprint className="h-4 w-4 mr-2" />
Sign in with passkey
</Button>
);
}