- Extracted `PasskeyTable` and `WebAuthnScript` into `ui/components/auth/`. - Refactored `PasskeysPage.tsx` and `RegisterPage.tsx` to use the new components instead of inline scripts and HTML. - Deleted the duplicate `ui/public/ui/utils/bip39_wordlist.ts` and `ui/public/ui/utils/bip39.ts`. - Updated all import references to use `ui/utils/bip39_wordlist.ts` and `/public/utils/bip39.ts`. Co-authored-by: mrteye <1945243+mrteye@users.noreply.github.com>
146 lines
5.5 KiB
TypeScript
146 lines
5.5 KiB
TypeScript
export const PasskeyTable = ({ passkeys }: { passkeys: any[] }) => {
|
|
return (
|
|
<>
|
|
{/* Desktop Table View (≥ 768px) */}
|
|
<div class="card desktop-only" style="display: none;">
|
|
<div class="table-container">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Credential ID</th>
|
|
<th>Sign Counter</th>
|
|
<th>AAGUID Vendor</th>
|
|
<th>Action</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{passkeys.length === 0
|
|
? (
|
|
<tr>
|
|
<td
|
|
colSpan={4}
|
|
style="text-align: center; padding: 2rem; color: var(--text-muted);"
|
|
>
|
|
No passkeys registered.
|
|
</td>
|
|
</tr>
|
|
)
|
|
: (
|
|
passkeys.map((passkey) => (
|
|
<tr key={passkey.id}>
|
|
<td>
|
|
<code style="background: var(--surface-muted); padding: 0.2rem 0.5rem; border-radius: var(--radius-sm); font-family: monospace; font-size: 0.85rem;">
|
|
{passkey.credential_id
|
|
? `${passkey.credential_id.substring(0, 16)}...`
|
|
: passkey.id}
|
|
</code>
|
|
</td>
|
|
<td style="color: var(--text-secondary);">
|
|
{passkey.counter}
|
|
</td>
|
|
<td>
|
|
<span class="badge badge-info">
|
|
{passkey.aaguid
|
|
? passkey.aaguid.substring(0, 8)
|
|
: "FIDO2 Key"}
|
|
</span>
|
|
</td>
|
|
<td>
|
|
<button
|
|
type="button"
|
|
class="btn-danger revoke-passkey-btn"
|
|
data-passkey-id={passkey.id}
|
|
disabled={passkeys.length <= 1}
|
|
style={passkeys.length <= 1
|
|
? "opacity: 0.5; cursor: not-allowed;"
|
|
: ""}
|
|
>
|
|
Remove
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
))
|
|
)}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Mobile Adaptive Cards View (< 768px) */}
|
|
<div
|
|
class="mobile-only"
|
|
style="display: flex; flex-direction: column; gap: 1rem; margin-bottom: 2rem;"
|
|
>
|
|
{passkeys.length === 0
|
|
? (
|
|
<div class="card" style="text-align: center; padding: 2rem;">
|
|
<p style="color: var(--text-muted); margin: 0;">
|
|
No passkeys registered.
|
|
</p>
|
|
</div>
|
|
)
|
|
: (
|
|
passkeys.map((passkey) => (
|
|
<div class="card" key={passkey.id} style="margin-bottom: 0;">
|
|
<div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 0.75rem;">
|
|
<div style="display: flex; align-items: center; gap: 0.65rem;">
|
|
<div style="display: flex; align-items: center; justify-content: center; width: 36px; height: 36px; background: var(--primary-light); color: var(--primary); border-radius: var(--radius-md);">
|
|
<svg
|
|
width="20"
|
|
height="20"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
>
|
|
<circle cx="7.5" cy="15.5" r="5.5"></circle>
|
|
<path d="m21 2-9.6 9.6"></path>
|
|
<path d="m15.5 7.5 3 3L22 7l-3-3"></path>
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<div style="font-weight: 700; color: var(--text-primary);">
|
|
Passkey Credential
|
|
</div>
|
|
<div style="font-size: 0.75rem; color: var(--text-muted); font-family: monospace;">
|
|
{passkey.credential_id
|
|
? `${passkey.credential_id.substring(0, 14)}...`
|
|
: passkey.id}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<span class="badge badge-info">
|
|
{passkey.aaguid ? "Hardware/OS" : "FIDO2"}
|
|
</span>
|
|
</div>
|
|
|
|
<div style="font-size: 0.8rem; color: var(--text-secondary); margin-bottom: 1rem;">
|
|
<div>
|
|
<strong>Counter:</strong> {passkey.counter} authentications
|
|
</div>
|
|
</div>
|
|
|
|
<button
|
|
type="button"
|
|
class="btn-danger revoke-passkey-btn"
|
|
data-passkey-id={passkey.id}
|
|
disabled={passkeys.length <= 1}
|
|
style={`width: 100%; min-height: 44px; ${
|
|
passkeys.length <= 1
|
|
? "opacity: 0.5; cursor: not-allowed;"
|
|
: ""
|
|
}`}
|
|
>
|
|
{passkeys.length <= 1
|
|
? "Cannot Remove Last Passkey"
|
|
: "Remove Passkey"}
|
|
</button>
|
|
</div>
|
|
))
|
|
)}
|
|
</div>
|
|
</>
|
|
);
|
|
};
|