VAPP-20: Security settings: list and remove passkeys, TOTP backup codes #25

Merged
David merged 2 commits from feat/security-passkeys-backup-codes-vapp-20 into main 2026-06-07 19:53:49 +02:00
Owner

VAPP-20: Security settings: list and remove passkeys, TOTP backup codes

Extends the Security card on the settings page so users can manage their second factors directly, all over the existing /control.ashx control channel.

Server functions (src/server_fns/mod.rs)

  • list_passkeys() reads name/id rows from otp-hkey-get (reply {keys:[{i, name, type}]}, metadata only, no credential material). i is vervain-server's keyIndex.
  • remove_passkey(id) deletes one key via otp-hkey-remove and resolves the ack (for: "otp-hkey-remove"); a non-ok result (e.g. settings-locked) surfaces inline.
  • get_backup_codes() mints a fresh batch via otpauth-getpasswords with subaction: 1, returning the one-time plaintext codes (passwords array of {p, u}). A null passwords (settings-locked / no TOTP) becomes an error. A fresh mint invalidates prior codes, so this also serves "regenerate".

Frame shapes were verified against vervain-server's crates/meshcentral-web/src/user_session.rs dispatch before wiring.

UI (src/components/pages/settings.rs)

  • The bare passkey count badge gains a per-key table listing each key by name with a two-click confirm-gated Remove action (same pattern as the TOTP disable flow). A successful add or remove restarts both the status resource and the new passkey-list resource, so the table and the count badge stay consistent without a reload.
  • A "Backup codes" row appears only while TOTP is enabled. It generates codes, renders them once in a monospace block with a copy-to-clipboard button, and warns that regenerating invalidates any previous codes.
  • Server errors (list, remove, generate) render inline via the existing ErrorText styling.

Acceptance criteria

  • Registered passkeys list by name; removing one requires a confirm click and updates the list and count without a reload
  • Adding a passkey refreshes the list (existing registration flow keeps working)
  • With TOTP enabled, backup codes can be generated, are displayed once with a copy button, and the UI warns that regenerating invalidates prior codes
  • With TOTP disabled, the backup codes section is absent
  • Server errors surface inline
  • just check passes

🤖 Generated with Claude Code

## VAPP-20: Security settings: list and remove passkeys, TOTP backup codes Extends the Security card on the settings page so users can manage their second factors directly, all over the existing `/control.ashx` control channel. ### Server functions (`src/server_fns/mod.rs`) - `list_passkeys()` reads name/id rows from `otp-hkey-get` (reply `{keys:[{i, name, type}]}`, metadata only, no credential material). `i` is vervain-server's `keyIndex`. - `remove_passkey(id)` deletes one key via `otp-hkey-remove` and resolves the `ack` (`for: "otp-hkey-remove"`); a non-`ok` result (e.g. `settings-locked`) surfaces inline. - `get_backup_codes()` mints a fresh batch via `otpauth-getpasswords` with `subaction: 1`, returning the one-time plaintext codes (`passwords` array of `{p, u}`). A `null` `passwords` (settings-locked / no TOTP) becomes an error. A fresh mint invalidates prior codes, so this also serves "regenerate". Frame shapes were verified against vervain-server's `crates/meshcentral-web/src/user_session.rs` dispatch before wiring. ### UI (`src/components/pages/settings.rs`) - The bare passkey count badge gains a per-key table listing each key by name with a two-click confirm-gated Remove action (same pattern as the TOTP disable flow). A successful add or remove restarts both the `status` resource and the new passkey-list resource, so the table and the count badge stay consistent without a reload. - A "Backup codes" row appears only while TOTP is enabled. It generates codes, renders them once in a monospace block with a copy-to-clipboard button, and warns that regenerating invalidates any previous codes. - Server errors (list, remove, generate) render inline via the existing `ErrorText` styling. ### Acceptance criteria - [x] Registered passkeys list by name; removing one requires a confirm click and updates the list and count without a reload - [x] Adding a passkey refreshes the list (existing registration flow keeps working) - [x] With TOTP enabled, backup codes can be generated, are displayed once with a copy button, and the UI warns that regenerating invalidates prior codes - [x] With TOTP disabled, the backup codes section is absent - [x] Server errors surface inline - [x] `just check` passes 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(security): list/remove passkeys and mint TOTP backup codes
All checks were successful
Check / fmt + clippy + build + tests (pull_request) Successful in 58s
44b352c9da
Extend the settings Security card so users can see their registered passkeys by name, remove individual keys, and generate TOTP backup codes, all over the existing control channel.

Server fns (src/server_fns/mod.rs): list_passkeys() reads name/id rows from otp-hkey-get; remove_passkey(id) deletes one key via otp-hkey-remove and surfaces the ack result inline; get_backup_codes() mints a fresh batch via otpauth-getpasswords (subaction 1), returning the one-time plaintext codes. Frame shapes were verified against vervain-server's user_session.rs dispatch (HkeyEntry {i,name}, ack for otp-hkey-remove, passwords array of {p,u}).

UI (src/components/pages/settings.rs): the bare passkey count badge gains a per-key table with a two-click confirm-gated Remove action (matching the TOTP disable pattern); a successful add or remove restarts both the status and the new passkey-list resource so the table and count stay consistent. A Backup codes row appears only while TOTP is enabled: it generates codes, shows them once in a monospace block with a copy-to-clipboard button, and warns that regenerating invalidates prior codes. Server errors render inline.

#VAPP-20

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Merge remote-tracking branch 'origin/main' into feat/security-passkeys-backup-codes-vapp-20
All checks were successful
Check / fmt + clippy + build + tests (pull_request) Successful in 1m0s
Create release / Create release from merged PR (pull_request) Has been skipped
b8c1745637
# Conflicts:
#	src/components/pages/settings.rs
David merged commit 0b26e92c88 into main 2026-06-07 19:53:49 +02:00
David deleted branch feat/security-passkeys-backup-codes-vapp-20 2026-06-07 19:53:50 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/vervain-apps!25
No description provided.