mirror of
https://github.com/shuaiplus/nodewarden.git
synced 2026-06-20 21:00:41 +00:00
feat: add FIDO2 credentials support in cipher handling and UI components
This commit is contained in:
@@ -16,6 +16,7 @@ import {
|
||||
draftFromCipher,
|
||||
buildCipherDuplicateSignature,
|
||||
firstCipherUri,
|
||||
firstPasskeyCreationTime,
|
||||
isCipherVisibleInArchive,
|
||||
isCipherVisibleInNormalVault,
|
||||
isCipherVisibleInTrash,
|
||||
@@ -971,6 +972,7 @@ function folderName(id: string | null | undefined): string {
|
||||
repromptApprovedCipherId={repromptApprovedCipherId}
|
||||
showPassword={showPassword}
|
||||
totpLive={totpLive}
|
||||
passkeyCreatedAt={firstPasskeyCreationTime(selectedCipher)}
|
||||
hiddenFieldVisibleMap={hiddenFieldVisibleMap}
|
||||
folderName={folderName}
|
||||
onOpenReprompt={() => setRepromptOpen(true)}
|
||||
|
||||
@@ -20,6 +20,7 @@ interface VaultDetailViewProps {
|
||||
repromptApprovedCipherId: string | null;
|
||||
showPassword: boolean;
|
||||
totpLive: { code: string; remain: number } | null;
|
||||
passkeyCreatedAt: string | null;
|
||||
hiddenFieldVisibleMap: Record<number, boolean>;
|
||||
folderName: (id: string | null | undefined) => string;
|
||||
downloadingAttachmentKey: string;
|
||||
@@ -135,6 +136,15 @@ export default function VaultDetailView(props: VaultDetailViewProps) {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!!props.passkeyCreatedAt && (
|
||||
<div className="kv-row">
|
||||
<span className="kv-label">{t('txt_passkey')}</span>
|
||||
<div className="kv-main">
|
||||
<strong>{t('txt_passkey_created_at_value', { value: formatHistoryTime(props.passkeyCreatedAt) })}</strong>
|
||||
</div>
|
||||
<div className="kv-actions" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -194,6 +194,9 @@ export function buildCipherDuplicateSignature(cipher: Cipher): string {
|
||||
uri: valueOrFallback(uri.decUri ?? uri.uri),
|
||||
match: uri.match ?? null,
|
||||
})),
|
||||
fido2Credentials: (cipher.login.fido2Credentials || []).map((credential) => ({
|
||||
creationDate: valueOrFallback(credential.creationDate),
|
||||
})),
|
||||
}
|
||||
: null,
|
||||
card: cipher.card
|
||||
@@ -262,6 +265,7 @@ export function createEmptyDraft(type: number): VaultDraft {
|
||||
loginPassword: '',
|
||||
loginTotp: '',
|
||||
loginUris: [createEmptyLoginUri()],
|
||||
loginFido2Credentials: [],
|
||||
cardholderName: '',
|
||||
cardNumber: '',
|
||||
cardBrand: '',
|
||||
@@ -310,6 +314,9 @@ export function draftFromCipher(cipher: Cipher): VaultDraft {
|
||||
uri: x.decUri || x.uri || '',
|
||||
match: x.match ?? null,
|
||||
}));
|
||||
draft.loginFido2Credentials = Array.isArray(cipher.login.fido2Credentials)
|
||||
? cipher.login.fido2Credentials.map((credential) => ({ ...credential }))
|
||||
: [];
|
||||
if (!draft.loginUris.length) draft.loginUris = [createEmptyLoginUri()];
|
||||
}
|
||||
if (cipher.card) {
|
||||
@@ -406,6 +413,16 @@ export function creationTimeValue(cipher: Cipher): number {
|
||||
return Number.isFinite(time) ? time : 0;
|
||||
}
|
||||
|
||||
export function firstPasskeyCreationTime(cipher: Cipher | null): string | null {
|
||||
const credentials = cipher?.login?.fido2Credentials;
|
||||
if (!Array.isArray(credentials) || credentials.length === 0) return null;
|
||||
for (const credential of credentials) {
|
||||
const raw = String(credential?.creationDate || '').trim();
|
||||
if (raw) return raw;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const failedIconHosts = new Set<string>();
|
||||
|
||||
export function VaultListIcon({ cipher }: { cipher: Cipher }) {
|
||||
|
||||
Reference in New Issue
Block a user