Build an API Key Manager widget that provides list, create, reveal-once, rotate, and revoke UX for API keys. The widget displays scoped permission badges, last-used metadata, and handles loading, empty, and destructive-confirmation states. It builds on the existing secret-reveal-field.tsx and secret-visibility-toggle.tsx primitives.
Introduction
Overview
SaaS applications universally need API key management: listing existing keys, creating new ones with scoped permissions, revealing a key exactly once after creation, rotating compromised keys, and revoking keys with confirmation. The design system provides SecretRevealField and SecretVisibilityToggle for secret display, but no composed widget for the full key lifecycle. Teams repeatedly build this pattern from scratch.
Goals
Provide a self-contained ApiKeyManager widget handling the full key lifecycle (list, create, reveal-once, rotate, revoke).
Display scoped permission badges per key.
Show last-used timestamp and creation date metadata.
Handle loading, empty, and error states with appropriate visual treatments.
Provide destructive-action confirmation dialogs for rotate and revoke.
Ship with comprehensive Storybook stories and unit tests.
Non-Goals
Actual API integration (key generation, storage); the widget is headless and calls consumer-provided callbacks.
OAuth token management (different UX pattern).
Rate limiting or usage analytics display.
Scope
In Scope
Item
Description
Key list view
DataGrid-based table showing key name, masked prefix, permissions, last used, created date, and actions.
Create key flow
Modal or slide-out panel with name input, permission scope checkboxes, and a generate action.
Reveal-once display
After creation, display the full key using SecretRevealField with auto-remask and copy-to-clipboard.
Rotate key
Confirmation dialog explaining that the old key will be invalidated; returns new key via callback.
Revoke key
Destructive confirmation dialog with key name echo; calls revoke callback.
SecretVisibilityToggle (src/components/ui/secret-visibility-toggle.tsx): Eye icon toggle for show/hide.
DataGrid (src/components/ui/data-grid.tsx): Full-featured table with sort, filter, paginate, row selection.
No API key management widget exists.
Badge component exists for permission display.
StatusCard exists for empty/error states.
AlertDialog exists for destructive confirmations.
Proposed Solution
Build src/components/widgets/api-key-manager.tsx as a composed widget:
Key List
Uses DataGrid with columns: Name, Key (masked prefix), Permissions (badge group), Last Used, Created, Actions (rotate/revoke dropdown). The list supports search filtering by key name.
Create Flow
A “Create API Key” button opens a SlideOutPanel or Dialog containing:
Name input (required).
Permission scope checkboxes (consumer provides available scopes).
Create and revoke dialogs trap focus and return focus to trigger on close.
A11Y-02
Revoke confirmation input has aria-label describing what to type.
A11Y-03
Permission badges use aria-label describing the permission scope.
A11Y-04
Reveal-once SecretRevealField announces “Key copied to clipboard” via aria-live on copy.
A11Y-05
DataGrid keyboard navigation works for all action buttons in the actions column.
A11Y-06
All components pass axe-core automated checks with zero violations.
Content and Documentation Requirements
ID
Requirement
DOC-01
Storybook docs page with usage guidelines, prop table, and interactive examples.
DOC-02
Recipes showing integration with TanStack Query for key fetching and mutation.
DOC-03
Security guidance: never log full keys, clear from state after reveal, use HTTPS-only.
Dependencies
Dependency
Type
Risk
src/components/ui/secret-reveal-field.tsx
Internal
Low — core reveal-once primitive.
src/components/ui/secret-visibility-toggle.tsx
Internal
Low — visibility toggle icon.
src/components/ui/data-grid.tsx
Internal
Low — table display.
src/components/ui/badge.tsx
Internal
Low — permission badges.
src/components/ui/status-card.tsx
Internal
Low — empty/error states.
src/components/ui/alert-dialog.tsx
Internal
Low — destructive confirmations.
Risks and Tradeoffs
Risk
Likelihood
Impact
Mitigation
Reveal-once key accidentally persists in React state or DevTools
Low
Critical
Clear key from state on panel close; use a ref instead of state for the raw key.
Revoke name-match confirmation is too strict (case sensitivity, whitespace)
Medium
Low
Trim and lowercase comparison; show the exact expected string.
Widget API is too opinionated for diverse backend shapes
Medium
Medium
Keep ApiKey interface minimal; consumers transform their data to fit.
Open Questions
#
Question
Owner
Status
OQ-01
Should the create flow use a Dialog or SlideOutPanel?
David Holmes
Open
OQ-02
Should the widget support bulk revoke (multi-select + revoke all)?
David Holmes
Open
OQ-03
Should key expiry be displayed as a badge with color coding (green/yellow/red)?
David Holmes
Open
Acceptance Criteria
#
Criterion
AC-01
Widget renders a list of API keys with name, masked prefix, permissions, last used, and actions.
AC-02
Create flow collects name and permissions, calls onCreate, and displays the full key exactly once.
AC-03
Reveal-once view includes copy button and warning text; key is cleared from state on close.
AC-04
Revoke flow requires typing the key name and calls onRevoke on confirmation.
AC-05
Loading, empty, and error states render appropriate visual treatments.
AC-06
All components pass axe-core checks with zero violations.
AC-07
Storybook stories exist for all states and interaction flows.
AC-08
Unit tests cover create, reveal-once, revoke confirmation, and state transitions.
AC-09
pnpm typecheck and pnpm vitest run --project unit pass with zero errors.
LLM Handoff Instructions
When implementing this FRD:
Createsrc/components/widgets/api-key-manager.tsx. Follow the pattern from people-table.tsx — define the data interface, column factory function, and composed widget.
DataGrid columns: Name (text), Key (masked prefix with monospace font), Permissions (badge group), Last Used (relative time), Created (date), Actions (dropdown with Rotate/Revoke).
Create flow: Use SlideOutPanel with a form containing TextField for name and a checkbox group for permissions. On submit, call onCreate and transition to reveal-once view.
Reveal-once: Render SecretRevealField with autoRemaskMs={undefined} (no auto-remask — the key stays visible until the user closes the panel). Add a copy button and warning text. On panel close, clear the key from a ref.
Revoke flow: Use AlertDialog with variant="destructive". Add a TextField where the user must type the key name. Disable the confirm button until the input matches.