key-cape/workplans/KEY-WP-0006-client-credentials-service-tokens.md
tegwick 881fffc079
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 25s
Complete KeyCape service-token rollout
2026-07-27 20:17:55 +02:00

4.1 KiB

id type title domain repo status owner topic_slug created updated state_hub_workstream_id
KEY-WP-0006 workplan Client credentials and service-token issuance infotech key-cape finished codex netkingdom 2026-07-27 2026-07-27 f2d5df23-9dd2-4995-b218-be5904e07508

KEY-WP-0006 - Client credentials and service tokens

Implement the missing service-principal half of the IAM Profile so KEY-WP-0004 can issue a least-privilege identity to rapp-qonto.

T01 - Model confidential service clients

id: KEY-WP-0006-T01
status: done
priority: high
state_hub_task_id: "4e7addc2-5773-499f-84e4-ffddc1800952"

Extend static client registration with an explicit service subject, tenant, roles, and an environment-backed secret reference. Refuse startup when a client_credentials client is public, lacks its identity fields, or cannot resolve its secret. Never serialize or log the resolved secret.

2026-07-27: Added explicit service subject, tenant, roles, and in-memory-only resolved secret fields. Configuration validation rejects public or incomplete service clients, and startup resolves only env: secret references while reporting the variable name—not its value—on failure.

T02 - Implement secure client_credentials exchange

id: KEY-WP-0006-T02
status: done
priority: high
state_hub_task_id: "9fd408dc-6f71-4765-ace2-22ee7bb1cf57"

Accept confidential client authentication through HTTP Basic, compare secrets in constant time, restrict requested scopes to the registered allowlist, and issue a short-lived access token with principal_type=service, tenant-specific subject/roles, empty groups, aal1 client-secret assurance, and optional cached tenant_roles. Do not issue an ID token for this non-human grant.

2026-07-27: Implemented HTTP Basic confidential-client authentication with fixed-length SHA-256 constant-time comparison, allowlisted scopes, short-lived service claims, optional tenant-role cache, token-issued telemetry, and no ID token.

T03 - Prove positive and negative conformance

id: KEY-WP-0006-T03
status: done
priority: high
state_hub_task_id: "e37a346b-f6bc-4b29-9c13-94ec1c79a381"

Cover valid exchange, unknown client, public client, missing/wrong secret, unsupported grant, excess scope, tenant isolation, telemetry redaction, and discovery metadata. Run the full Go build, vet, and test suite.

2026-07-27: Added positive service-claim and negative wrong-secret/excess-scope tests, updated discovery to advertise only implemented grants/auth methods, and verified go test ./..., go vet ./..., and go build ./... across the module.

T04 - Provision and verify rapp-qonto

id: KEY-WP-0006-T04
status: done
priority: high
state_hub_task_id: "ba65d39f-63b8-42aa-87bb-99ec26821a8e"

Generate the client secret without disclosure, store it through the approved OpenBao lane, register the static rapp-qonto client, deploy KeyCape, and verify a real exchange yields only the documented Binky service claims. Publish non-secret evidence to KEY-WP-0004-T03 through T05.

2026-07-27: Provisioned the confidential client secret at platform/workloads/rapp-qonto/keycape-client, injected it into sso/keycape-rapp-qonto-client, registered rapp-qonto-client, and deployed image main-e877d27-2 on railiance01. A real exchange produced only sub=rapp-qonto, tenant=tenant:friendly:binky, principal_type=service, roles=[qonto-reader], empty groups, and scope=qonto:read; excessive scope and a wrong secret were denied. Public DNS still resolves kc.coulomb.social to the older CoulombCore endpoint, so the deployment proof used TLS-preserving direct resolution to railiance01 (92.205.62.239).

T05 - Closure review

id: KEY-WP-0006-T05
status: done
priority: low
state_hub_task_id: "7c9cdac6-c53f-4a32-81d2-8d2e73de0039"

Close after T01-T04 pass and the repeatable verification path is documented.

2026-07-27: Closure review passed. Build, vet, unit/profile tests, live positive claims, negative authentication/scope checks, and discovery metadata all passed. The stale public DNS target is handed off as routing work rather than weakening the service-token contract.