plan(phase1): ScadaBridge ApiKeys full-adopt re-arch spec + sub-task decomposition
This commit is contained in:
@@ -142,6 +142,35 @@ a guard/contract test.
|
||||
→ Bearer, per-method-approval → scopes/constraints, **all inbound keys re-issued**). Largest/highest-risk
|
||||
single item in the program; warrants its own focused pass (likely decomposed).
|
||||
|
||||
## ScadaBridge ApiKeys re-architecture — spec (FULL ADOPT, 2026-06-02)
|
||||
|
||||
Decision: **full adopt** the library SQLite store + scopes model. Single consistent contract all layers build to:
|
||||
|
||||
- **Token format**: `Authorization: Bearer sbk_<keyId>_<secret>` (prefix `sbk`). Replaces the raw `X-API-Key` header.
|
||||
- **Scope model = method name.** A key's `Scopes` set = the API-method names it may call. `ApiMethod.ApprovedApiKeyIds`
|
||||
(CSV of key int IDs) is **retired**; per-method approval moves to the key's scopes. Auth check at the endpoint:
|
||||
`identity.Scopes.Contains(methodName)`.
|
||||
- **Storage**: inbound keys move to the library's SQLite store (new `ScadaBridge:InboundApi:ApiKeyStore` sqlite path
|
||||
+ pepper via `ApiKeyOptions.PepperSecretName`, `RunMigrationsOnStartup`). The SQL Server `ApiKey` entity is retired;
|
||||
`ApiMethod` is KEPT minus `ApprovedApiKeyIds` (EF migration drops the column). `InboundApiRepository` loses its ApiKey
|
||||
methods + `GetApprovedKeysForMethodAsync`.
|
||||
- **Auth path** (`InboundAPI`): endpoint reads Bearer, calls library `IApiKeyVerifier.VerifyAsync`, then the scope check.
|
||||
PRESERVE the security invariants: 401 (missing/invalid/disabled), **403 identical message for both "method not found"
|
||||
and "not in scope"** (enumeration-safety, InboundAPI-011), constant-time compare (library does it), active-node 503 +
|
||||
body-cap 413 filters unchanged, audit actor = key DisplayName. Delete `ApiKeyValidator` hashing + `ApiKeyHasher`.
|
||||
- **Management** (`ManagementActor` + CLI `security api-key` + Commons messages): drive the library `IApiKeyAdminStore` +
|
||||
`ApiKeySecretGenerator`. `create` returns `sbk_<keyId>_<secret>` once (plaintext-once preserved); methods a key may call
|
||||
= its scopes, set on create/update (e.g. `--methods a,b` or grant/revoke-method commands). `list` returns id/name/enabled
|
||||
(no secret), `update --enabled`, `delete`/revoke. Audit preserved.
|
||||
- **CentralUI**: `ApiKeys.razor` (list/create/toggle/delete via admin store; show token once), `ApiKeyForm.razor` (edit the
|
||||
key's method-scopes), `ApiMethodForm.razor` (method-side "approved keys" now reads/writes key scopes across keys).
|
||||
- **Breaking change**: all inbound keys re-issued (new format); clients switch `X-API-Key` → `Authorization: Bearer`.
|
||||
Needs a runbook + CHANGELOG. Re-pin ScadaBridge Auth packages to **0.1.2**.
|
||||
|
||||
Sub-tasks (sequential where files overlap): **(A)** storage retire + EF migration + library wiring/options;
|
||||
**(B)** auth-path rewrite (Bearer + verifier + scope check); **(C)** management (ManagementActor + CLI + messages);
|
||||
**(D)** CentralUI pages; **(E)** runbook/CHANGELOG + integration test sweep. A→(B,C)→D→E.
|
||||
|
||||
## Resolved decisions (2026-06-02)
|
||||
|
||||
- **Decision A — ScadaBridge inbound API keys depth → (a) FULL ADOPT.** Re-architect inbound-API auth to the
|
||||
|
||||
Reference in New Issue
Block a user