feat: Add YubiKey OTP support and management features

- Implemented YubiKey OTP settings management in useAccountSecurityActions hook.
- Added API functions for retrieving, saving, and bootstrapping YubiKey OTP credentials.
- Enhanced authentication flow to support multiple two-factor providers, including YubiKey.
- Updated localization files to include new YubiKey-related strings in English, Spanish, Russian, and Chinese.
- Introduced new styles for YubiKey management UI components.
- Created utility functions for YubiKey OTP validation and credential handling.
This commit is contained in:
shuaiplus
2026-07-04 02:49:46 +08:00
parent c7eb6c663d
commit f63b745d05
27 changed files with 1325 additions and 61 deletions
+25
View File
@@ -34,6 +34,7 @@ export interface PendingTotp {
passwordHash: string;
masterKey: Uint8Array;
kdfIterations: number;
providerType: number;
}
export interface PendingPasskeyPassword {
@@ -70,10 +71,31 @@ export interface CompletedLogin {
freshUserVerificationToken?: string | null;
}
const TWO_FACTOR_PROVIDER_AUTHENTICATOR = 0;
const TWO_FACTOR_PROVIDER_YUBIKEY = 3;
function readTokenUserVerificationToken(token: TokenSuccess): string | null {
return String(token.UserVerificationToken || token.userVerificationToken || '').trim() || null;
}
function resolvePendingTwoFactorProvider(providers: unknown): number {
if (Array.isArray(providers)) {
if (providers.some((provider: any) => Number(
provider && typeof provider === 'object' ? provider.Type ?? provider.type : provider
) === TWO_FACTOR_PROVIDER_YUBIKEY)) {
return TWO_FACTOR_PROVIDER_YUBIKEY;
}
return TWO_FACTOR_PROVIDER_AUTHENTICATOR;
}
if (providers && typeof providers === 'object') {
const record = providers as Record<string, unknown>;
if (record[String(TWO_FACTOR_PROVIDER_YUBIKEY)] || record.YubiKey || record.Yubikey) {
return TWO_FACTOR_PROVIDER_YUBIKEY;
}
}
return TWO_FACTOR_PROVIDER_AUTHENTICATOR;
}
export type PasswordLoginResult =
| { kind: 'success'; login: CompletedLogin }
| { kind: 'totp'; pendingTotp: PendingTotp }
@@ -425,6 +447,7 @@ export async function performPasswordLogin(
passwordHash: derived.hash,
masterKey: derived.masterKey,
kdfIterations: derived.kdfIterations,
providerType: resolvePendingTwoFactorProvider(tokenError.TwoFactorProviders),
},
};
}
@@ -498,6 +521,7 @@ export async function performTotpLogin(
): Promise<CompletedLogin> {
const token = await loginWithPassword(pendingTotp.email, pendingTotp.passwordHash, {
totpCode: totpCode.trim(),
twoFactorProvider: pendingTotp.providerType,
rememberDevice,
});
if ('access_token' in token && token.access_token) {
@@ -615,6 +639,7 @@ export async function performUnlock(
passwordHash: derived.hash,
masterKey: derived.masterKey,
kdfIterations: derived.kdfIterations,
providerType: resolvePendingTwoFactorProvider(tokenError.TwoFactorProviders),
},
};
}