You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The linked initiative is authoritative and contains the complete design, risks, tests, acceptance criteria, and focused PR sequence.
Core authorization invariant
allowed(operation, resource)
= ownerCurrentlyAllows(operation, resource)
AND tokenGrantAllows(operation, currentAuthorizationBoundary(resource))
Persist explicit operation + Product/Folder/global-boundary grant pairs. Never store permissions and scope as freely recombinable sets.
Use the actual HSM model:
IsAdmin is global.
ProductManager and ProductViewer are per Product/Folder.
No global Client role or disabled/blocked user state exists.
Owner deletion, downgrade, or resource-role removal affects the next request.
An IsAdmin owner can create a read-only token whose covered mutations all return 403.
An existing token cannot expand operation/boundary pairs, including through rotation. An explicitly confirmed Folder grant intentionally includes current and future descendants; this is the only dynamic-membership exception.
Token and verifier contract
Format: hsm_pat_v1_..
TokenId: 128 random bits, exactly 22 canonical unpadded Base64URL characters.
Secret: 256 random bits from RandomNumberGenerator.GetBytes(32), exactly 43 canonical unpadded Base64URL characters.
Reject padding, wrong lengths/alphabet, aliases, and non-canonical encoding.
hsm_pat_v1_ maps only to versionByte 0x01 and must match the stored record.
No pepper, PepperKeyId, pepper configuration/rotation, or separate verification secret.
Always hash the presented candidate. Compare against stored-or-fixed-dummy verifier with FixedTimeEquals. A missing-record marker forces generic failure after comparison.
Server generates every secret; user-selected secrets are forbidden.
Credential-safe v1 capability boundary
The v1 token catalog contains no users:, access-keys:, credentials:, or server-settings: capability.
Grantable management APIs:
use dedicated credential-free DTOs;
never serialize User, UserViewModel, AccessKeyModel, or AccessKeyViewModel;
never return User.Password, operative access-key Id/master key, token, verifier, secret, or reversible authentication material.
A future metadata-only user/access-key/settings capability requires a separate threat review and purpose-built DTO.
Principal and listener isolation
Cookie remains DefaultAuthenticateScheme, DefaultChallengeScheme, and the scheme pinned by DefaultPolicy/bare Authorize.
HsmApiToken is never default, forwarded, or a policy scheme. It runs only from explicit management policies.
Ordinary data-management /api/v1 routes require only HsmApiToken and reject cookie-only principals.
/api/v1/api-tokens is cookie-only and rejects all API-token principals.
A non-/api/v1 hsm_pat_ bearer guard returns non-redirecting generic 401 before MVC/Razor/BaseController, performs no token lookup, and never produces 500.
A token principal has exactly one HsmApiToken identity. Mixed/multiple identities fail closed.
UserProcessorMiddleware never replaces a token principal and the handler never sets Identity.Name to a login.
Management controllers derive from ControllerBase, not BaseController.
/api/v1 is SitePort-only and rejected on SensorPort through fail-closed endpoint metadata.
Preserve existing collector/Grafana OpenAPI, Swagger UI, and Key/ClientName behavior on both listeners. Management v1 has a separate SitePort-only document and does not inherit DataRequestHeaderSwaggerFilter.
Persistence identity and insertion
ApiTokenEntity includes:
EntityId: stable GUID for lifecycle routes, in-memory entity identity, and journal.
TokenId: public 128-bit authentication lookup key.
Generation-invalidated records immediately stop counting toward MaxTokensPerUser, even before per-record reconciliation.
Lifecycle rules
Create returns the full token exactly once with Cache-Control: no-store.
Restrict only removes grant pairs or shortens expiry.
Rotate preserves/reduces grants and expiry, creates fresh EntityId/TokenId/secret, atomically revokes the old token, and cannot make finite expiry unlimited.
API tokens cannot create/list/restrict/rotate/revoke tokens.
ApiTokens.Enabled=false rejects token auth and issuance/rotation/restrict while cookie list/revoke and emergency actions remain available.
MaxTokensPerUser counts only unexpired, individually active, generation-current records.
Retention cleanup is bounded and independent from lifecycle/security-event audit retention.
Current owner rights and current hierarchy are checked on every request.
Folder grants store stable Folder kind/ID, warn/confirm dynamic descendant membership, and fail closed after deletion/recreation with another ID.
Delivery tasks
ADR, /api/v1 convention, capability/role matrix, operation-boundary catalog, and separate management OpenAPI group.
Context
Implement the authentication foundation for the AI-manageable HSM API: fine-grained opaque API tokens (PAT-style), not OAuth/JWT.
Normative source:
The linked initiative is authoritative and contains the complete design, risks, tests, acceptance criteria, and focused PR sequence.
Core authorization invariant
Persist explicit operation + Product/Folder/global-boundary grant pairs. Never store permissions and scope as freely recombinable sets.
Use the actual HSM model:
An existing token cannot expand operation/boundary pairs, including through rotation. An explicitly confirmed Folder grant intentionally includes current and future descendants; this is the only dynamic-membership exception.
Token and verifier contract
SHA-256(ASCII("HSM-API-TOKEN") || 0x00 || versionByte[1] || tokenId[16] || secret[32]).Credential-safe v1 capability boundary
The v1 token catalog contains no users:, access-keys:, credentials:, or server-settings: capability.
Grantable management APIs:
A future metadata-only user/access-key/settings capability requires a separate threat review and purpose-built DTO.
Principal and listener isolation
Persistence identity and insertion
ApiTokenEntity includes:
Lifecycle routes use {entityId}; list/detail returns EntityId plus a safe display hint, never full TokenId/secret/verifier.
Implement dedicated persist-first TryInsertApiToken:
Emergency revocation
Cookie-only IsAdmin routes:
Require CSRF, sanitized reason, and explicit target confirmation. Remain available when ApiTokens.Enabled=false.
Mechanism:
HTTP mapping: 204 success, 400 confirmation/reason, 401 no cookie, 403 non-admin, 404 unknown owner, 503 storage/publication failure.
Generation-invalidated records immediately stop counting toward MaxTokensPerUser, even before per-record reconciliation.
Lifecycle rules
Delivery tasks
Mandatory acceptance examples
Use the complete acceptance and verification matrix in the linked initiative. Deliver the focused PR sequence rather than one large implementation PR.