Document the dashboard API Keys management page

The dashboard's API Keys page (list plus Create/Rotate/Revoke and the
create dialog) had no design-doc coverage even though Authorization.md
already documents the constraint model it exposes. Add an "API keys
page" section to GatewayDashboardDesign.md describing the table columns,
the LDAP-group-gated management actions, the one-time secret reveal, and
audit logging. Cross-link it from the constraint-enforcement section of
Authorization.md and the CLI section of Authentication.md so the two
key-management surfaces reference each other.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-05-17 01:07:35 -04:00
parent f598b3a647
commit 3397e99783
3 changed files with 63 additions and 0 deletions
+5
View File
@@ -223,6 +223,10 @@ constraints remain fully unconstrained after migration.
Key ids are restricted by the parser to ASCII letters, digits, periods, and hyphens so they remain safe to embed in the token format and in URL paths used by administrative tooling.
The CLI is not the only management surface: the dashboard API Keys page
creates, rotates, and revokes keys through the same `IApiKeyAdminStore`. See
[Gateway Dashboard Design](./GatewayDashboardDesign.md#api-keys-page).
## Scope Serialization
Scopes are persisted as a single TEXT column rather than a join table because the set is small, never queried by membership at the database level, and changes atomically with the owning row. `ApiKeyScopeSerializer.Serialize` writes a JSON array sorted with `StringComparer.Ordinal` so equivalent scope sets produce byte-identical column values, which makes audit diffing and database comparisons deterministic:
@@ -276,4 +280,5 @@ Singletons are safe because each operation opens its own short-lived `SqliteConn
- [Gateway Configuration](./GatewayConfiguration.md)
- [Authorization](./Authorization.md)
- [Gateway Dashboard Design](./GatewayDashboardDesign.md)
- [Diagnostics](./Diagnostics.md)
+7
View File
@@ -161,6 +161,12 @@ Glob matching is anchored, case-insensitive, and supports `*` and `?`.
Subtree and tag glob lists are alternatives: matching either list allows that
scope dimension. Empty lists mean unconstrained for that dimension.
Constraints are set when a key is created — through the `apikey create-key`
flags (see [Authentication](./Authentication.md)) or the dashboard API Keys
page create dialog (see
[Gateway Dashboard Design](./GatewayDashboardDesign.md#api-keys-page)). The
dashboard API Keys page also renders each key's effective constraints.
The service checks read constraints for `AddItem`, `AddItem2`, `AddItemBulk`,
`SubscribeBulk`, and `AdviseItemBulk`. It checks write constraints for
`Write`, `Write2`, `WriteSecured`, and `WriteSecured2`. Successful item
@@ -252,6 +258,7 @@ Singleton lifetimes are appropriate because none of the three classes hold per-r
## Related Documentation
- [Authentication](./Authentication.md)
- [Gateway Dashboard Design](./GatewayDashboardDesign.md)
- [Grpc](./Grpc.md)
- [GatewayConfiguration](./GatewayConfiguration.md)
- [Galaxy Repository Browse](./GalaxyRepository.md)
+51
View File
@@ -49,6 +49,7 @@ Endpoint layout:
/dashboard/workers
/dashboard/events
/dashboard/galaxy
/dashboard/apikeys
/dashboard/settings
/dashboard/_blazor
```
@@ -83,6 +84,7 @@ MxGateway.Server
SessionDetailsPage.razor
WorkersPage.razor
EventsPage.razor
ApiKeysPage.razor
SettingsPage.razor
Shared/
MetricCard.razor
@@ -91,6 +93,9 @@ MxGateway.Server
DashboardSnapshotService.cs
DashboardAuthorizationHandler.cs
DashboardAuthenticator.cs
DashboardApiKeyAuthorization.cs
DashboardApiKeyManagementService.cs
DashboardApiKeySummary.cs
DashboardSnapshot.cs
DashboardSessionSummary.cs
DashboardWorkerSummary.cs
@@ -249,6 +254,52 @@ Show aggregate event diagnostics:
Do not display full tag values by default. If value display is later added, make
it opt-in and redacted.
### API keys page
`/dashboard/apikeys` lists the gateway's API keys and, for authorized
operators, manages them. It reads key metadata through the same
`IApiKeyAdminStore` the `apikey` CLI uses, so the dashboard and the CLI act
on one source of truth.
The table shows one row per key:
- key id,
- status (`Active` or `Revoked`),
- display name,
- scopes,
- constraints (rendered as `unconstrained` when none are set),
- created timestamp,
- last-used timestamp.
Key secrets are never listed. Only the peppered hash is stored, and the page
never reconstructs a key. See [Authorization](./Authorization.md#constraint-enforcement)
for what each constraint means and how it is enforced on the gRPC path.
#### Management actions
Create, Rotate, and Revoke controls render only when the signed-in user is
authorized. `DashboardApiKeyAuthorization.CanManage` requires an authenticated
principal that is a member of the LDAP `MxGateway:Ldap:RequiredGroup` — the
same group the dashboard login enforces. An anonymous localhost viewer can read
the table but sees no action controls.
- **Create** opens a dialog for the key id, display name, scope checkboxes
(the `GatewayScopes` catalog), and the optional constraint fields: read and
write subtrees, read and write tag globs, browse subtrees, max write
classification, and the read-alarm-only / read-historized-only flags.
- **Rotate** issues a new secret for an existing key id and invalidates the
old one.
- **Revoke** marks a key revoked; a revoked key cannot be un-revoked.
Create and Rotate return the assembled `mxgw_<keyId>_<secret>` token **once**,
in a one-time banner. It is never shown again, so the operator must copy it
immediately. This mirrors the `apikey create-key` / `rotate-key` CLI.
Every management action appends an `api_key_audit` entry
(`dashboard-create-key`, `dashboard-rotate-key`, `dashboard-revoke-key`) with
the key id and the caller's remote address. Secrets and pepper values are never
logged.
### Settings page
Show read-only effective configuration: