passkey translations

This commit is contained in:
Pas 2025-12-29 16:31:28 -07:00
parent b7e08b505f
commit 4a8c756eb0
5 changed files with 51 additions and 42 deletions

View file

@ -158,7 +158,8 @@
"customPassphrasePlaceholder": "Enter your custom passphrase",
"useCustomPassphrase": "Use Custom Passphrase",
"invalidPassphraseCharacters": "Invalid passphrase characters. Only English letters, numbers 1-10, and normal symbols are allowed.",
"passphraseTooShort": "Passphrase must be at least 8 characters long."
"passphraseTooShort": "Passphrase must be at least 8 characters long.",
"usePasskeyInstead": "Use passkey instead"
},
"hasAccount": "Already have an account? <0>Login here.</0>",
"login": {
@ -168,7 +169,10 @@
"passphrasePlaceholder": "Passphrase",
"submit": "Login",
"title": "Login to your account",
"validationError": "Incorrect or incomplete passphrase /ᐠ. .ᐟ\\"
"validationError": "Incorrect or incomplete passphrase /ᐠ. .ᐟ\\",
"usePasskey": "Use passkey",
"or": "or",
"noBackendUrl": "No backend URL"
},
"register": {
"information": {
@ -209,7 +213,9 @@
"passphraseLabel": "Your 12-word passphrase",
"recaptchaFailed": "ReCaptcha validation failed",
"register": "Create account",
"title": "Confirm your passphrase"
"title": "Confirm your passphrase",
"passkeyDescription": "Please authenticate with your passkey to complete registration.",
"authenticatePasskey": "Authenticate with Passkey"
}
},
"errors": {

View file

@ -156,7 +156,12 @@ export function decryptData(data: string, secret: Uint8Array) {
// Passkey/WebAuthn utilities
export function isPasskeySupported(): boolean {
// Passkeys require HTTPS
const isSecureContext =
typeof window !== "undefined" && window.location.protocol === "https:";
return (
isSecureContext &&
typeof navigator !== "undefined" &&
"credentials" in navigator &&
"create" in navigator.credentials &&

View file

@ -117,8 +117,40 @@ export function LoginFormPart(props: LoginFormPartProps) {
{t("auth.login.description")}
</LargeCardText>
<div className="space-y-4">
<AuthInputBox
label={t("auth.deviceNameLabel") ?? undefined}
value={device}
onChange={setDevice}
placeholder={t("auth.deviceNamePlaceholder") ?? undefined}
/>
<AuthInputBox
label={t("auth.login.passphraseLabel") ?? undefined}
value={mnemonic}
autoComplete="username"
name="username"
onChange={setMnemonic}
placeholder={t("auth.login.passphrasePlaceholder") ?? undefined}
passwordToggleable
/>
{(result.error || passkeyResult.error) &&
!result.loading &&
!passkeyResult.loading ? (
<p className="text-authentication-errorText">
{result.error?.message || passkeyResult.error?.message}
</p>
) : null}
{isPasskeySupported() && (
<div>
<div className="relative mb-4">
<div className="relative my-4">
<div className="absolute inset-0 flex items-center">
<div className="w-full border-t border-authentication-border/50" />
</div>
<div className="relative flex justify-center text-sm">
<span className="px-2 bg-authentication-bg text-authentication-text">
{t("auth.login.or")}
</span>
</div>
</div>
<Button
theme="secondary"
onClick={() => executePasskey(device)}
@ -131,42 +163,10 @@ export function LoginFormPart(props: LoginFormPartProps) {
className="w-full"
>
<Icon icon={Icons.LOCK} className="mr-2" />
{t("auth.login.usePasskey") ?? "Use passkey"}
{t("auth.login.usePasskey")}
</Button>
<div className="relative my-4">
<div className="absolute inset-0 flex items-center">
<div className="w-full border-t border-authentication-border/50" />
</div>
<div className="relative flex justify-center text-sm">
<span className="px-2 bg-authentication-bg text-authentication-text">
{t("auth.login.or") ?? "or"}
</span>
</div>
</div>
</div>
)}
<AuthInputBox
label={t("auth.login.passphraseLabel") ?? undefined}
value={mnemonic}
autoComplete="username"
name="username"
onChange={setMnemonic}
placeholder={t("auth.login.passphrasePlaceholder") ?? undefined}
passwordToggleable
/>
<AuthInputBox
label={t("auth.deviceNameLabel") ?? undefined}
value={device}
onChange={setDevice}
placeholder={t("auth.deviceNamePlaceholder") ?? undefined}
/>
{(result.error || passkeyResult.error) &&
!result.loading &&
!passkeyResult.loading ? (
<p className="text-authentication-errorText">
{result.error?.message || passkeyResult.error?.message}
</p>
) : null}
</div>
<LargeCardButtons>

View file

@ -80,7 +80,7 @@ export function PassphraseGeneratePart(props: PassphraseGeneratePartProps) {
className="w-full"
>
<Icon icon={Icons.LOCK} className="mr-2" />
{t("auth.generate.usePasskeyInstead") ?? "Use passkey instead"}
{t("auth.generate.usePasskeyInstead")}
</Button>
{passkeyResult.error && (
<p className="mt-2 text-authentication-errorText text-sm text-center">

View file

@ -188,8 +188,7 @@ export function VerifyPassphrase(props: VerifyPassphraseProps) {
icon={<Icon icon={Icons.CIRCLE_CHECK} />}
title={t("auth.verify.title")}
>
{t("auth.verify.passkeyDescription") ??
"Please authenticate with your passkey to complete registration."}
{t("auth.verify.passkeyDescription")}
</LargeCardText>
{passkeyResult.error ? (
<p className="mt-3 text-authentication-errorText">
@ -203,8 +202,7 @@ export function VerifyPassphrase(props: VerifyPassphraseProps) {
onClick={() => authenticatePasskeyFn()}
>
<Icon icon={Icons.LOCK} className="mr-2" />
{t("auth.verify.authenticatePasskey") ??
"Authenticate with Passkey"}
{t("auth.verify.authenticatePasskey")}
</Button>
</LargeCardButtons>
</form>