feat(apikeys): optional ExpiresUtc — create + verifier enforcement + sqlite v3 migration (archreview G-2)

ApiKeyRecord/ApiKeyListItem gain a nullable ExpiresUtc (NULL = never
expires); ApiKeyFailure gains KeyExpired. ApiKeyVerifier rejects a key
whose ExpiresUtc is at-or-before now (inclusive, injected clock), before
the secret comparison. CreateKeyAsync gets an expiresUtc overload; rotate
preserves existing expiry. SQLite schema bumps to v3: a nullable
expires_utc column, added to fresh DBs via CREATE and to existing v1/v2
DBs via an idempotent guarded ALTER — donor v2 gateway-auth.db upgrades
in place, no key invalidated. Version 0.1.3 -> 0.1.4 (not yet published;
nuget push is human-gated). Consumers (HistorianGateway, mxaccessgw) bump
to 0.1.4 to set/enforce expiry. 146 Auth.ApiKeys tests pass.
This commit is contained in:
Joseph Doherty
2026-07-09 06:04:54 -04:00
parent 497c0aac52
commit 378880f0ed
14 changed files with 381 additions and 32 deletions
+9 -1
View File
@@ -29,7 +29,15 @@ Authentication and authorisation libraries for the **ZB.MOM.WW SCADA family** (O
## Versioning
All four packages are versioned **lockstep** from `Directory.Build.props`. The current release is **0.1.0**. A single version bump in `Directory.Build.props` bumps all four packages simultaneously — consumers should reference the same version for all ZB.MOM.WW.Auth packages.
All four packages are versioned **lockstep** from `Directory.Build.props`. The current release is **0.1.4**. A single version bump in `Directory.Build.props` bumps all four packages simultaneously — consumers should reference the same version for all ZB.MOM.WW.Auth packages.
### 0.1.4 — API-key expiry (archreview G-2)
- **`ApiKeyRecord`/`ApiKeyListItem` gain an optional `DateTimeOffset? ExpiresUtc`** (nullable; `NULL` = never expires). `ApiKeyFailure` gains `KeyExpired`.
- **`ApiKeyVerifier` enforces expiry** — a key whose `ExpiresUtc` is at or before "now" (inclusive, injected clock) fails with `KeyExpired`, decided before the secret comparison. Existing (never-expiring) keys are unaffected.
- **`ApiKeyAdminCommands.CreateKeyAsync`** has a new overload taking `DateTimeOffset? expiresUtc`; the old signature delegates with `null`. `RotateKeyAsync` deliberately preserves an existing expiry (rotation changes only the secret).
- **SQLite schema → v3**: a nullable `expires_utc` column, added to fresh DBs by the CREATE DDL and to existing v1/v2 DBs by an idempotent `ALTER TABLE` in the migrator (existing rows → `NULL`). A donor v2 `gateway-auth.db` is accepted (2 ≤ 3) and upgraded in place — no key is invalidated. Old-build readers tolerate the extra column.
- **Consumers must bump to 0.1.4** to set/enforce expiry (mxaccessgw + HistorianGateway benefit from the same verifier).
---