diff --git a/WORK-RECORDS.md b/WORK-RECORDS.md index 138decf..8b79de3 100644 --- a/WORK-RECORDS.md +++ b/WORK-RECORDS.md @@ -58,8 +58,8 @@ | task | KEY-WP-0005-T01 | done | — | workplans/KEY-WP-0005-iam-profile-core-claims.md | | task | KEY-WP-0005-T02 | done | — | workplans/KEY-WP-0005-iam-profile-core-claims.md | | task | KEY-WP-0005-T03 | done | — | workplans/KEY-WP-0005-iam-profile-core-claims.md | -| task | KEY-WP-0006-T01 | progress | — | workplans/KEY-WP-0006-client-credentials-service-tokens.md | -| task | KEY-WP-0006-T02 | todo | — | workplans/KEY-WP-0006-client-credentials-service-tokens.md | -| task | KEY-WP-0006-T03 | todo | — | workplans/KEY-WP-0006-client-credentials-service-tokens.md | -| task | KEY-WP-0006-T04 | wait | — | workplans/KEY-WP-0006-client-credentials-service-tokens.md | +| task | KEY-WP-0006-T01 | done | — | workplans/KEY-WP-0006-client-credentials-service-tokens.md | +| task | KEY-WP-0006-T02 | done | — | workplans/KEY-WP-0006-client-credentials-service-tokens.md | +| task | KEY-WP-0006-T03 | done | — | workplans/KEY-WP-0006-client-credentials-service-tokens.md | +| task | KEY-WP-0006-T04 | progress | — | workplans/KEY-WP-0006-client-credentials-service-tokens.md | | task | KEY-WP-0006-T05 | wait | — | workplans/KEY-WP-0006-client-credentials-service-tokens.md | diff --git a/src/internal/server/errors/enforcement.go b/src/internal/server/errors/enforcement.go index 03f875b..81fd2c8 100644 --- a/src/internal/server/errors/enforcement.go +++ b/src/internal/server/errors/enforcement.go @@ -182,7 +182,7 @@ func DefaultRegistry() *Registry { reg.Register(UnsupportedFeature{ Name: "unknown_grant_type", ErrorType: profileerrors.ErrFeatureNotSupported, - Description: "Only authorization_code and refresh_token grant types are supported.", + Description: "Only authorization_code and client_credentials grant types are supported.", Detector: func(r *http.Request) bool { if r.Method != http.MethodPost || !strings.HasSuffix(r.URL.Path, "/token") { return false @@ -195,7 +195,7 @@ func DefaultRegistry() *Registry { if gt == "" { return false // no grant_type present; let the handler decide } - return gt != "authorization_code" && gt != "refresh_token" + return gt != "authorization_code" && gt != "client_credentials" }, }) diff --git a/src/internal/server/errors/enforcement_test.go b/src/internal/server/errors/enforcement_test.go index 6fe84e8..0f8384b 100644 --- a/src/internal/server/errors/enforcement_test.go +++ b/src/internal/server/errors/enforcement_test.go @@ -155,7 +155,7 @@ func TestDefaultRegistry_UnknownGrantType(t *testing.T) { em := newRecEmitter() handler := reg.Middleware(alwaysOK()) - w := serve(handler, reqWithEmitter(http.MethodPost, "/token?grant_type=client_credentials", em)) + w := serve(handler, reqWithEmitter(http.MethodPost, "/token?grant_type=password", em)) assertProfileError(t, w, profileerrors.ErrFeatureNotSupported, "unknown_grant_type") assertTelemetryEmitted(t, em, "unknown_grant_type") @@ -165,7 +165,7 @@ func TestDefaultRegistry_UnknownGrantType_AllowedTypes(t *testing.T) { reg := serverrors.DefaultRegistry() handler := reg.Middleware(alwaysOK()) - for _, gt := range []string{"authorization_code", "refresh_token"} { + for _, gt := range []string{"authorization_code", "client_credentials"} { req := reqWithEmitter(http.MethodPost, "/token?grant_type="+gt, newRecEmitter()) w := serve(handler, req) if w.Code != http.StatusOK { diff --git a/workplans/KEY-WP-0006-client-credentials-service-tokens.md b/workplans/KEY-WP-0006-client-credentials-service-tokens.md index ac24e7b..cac2e97 100644 --- a/workplans/KEY-WP-0006-client-credentials-service-tokens.md +++ b/workplans/KEY-WP-0006-client-credentials-service-tokens.md @@ -4,7 +4,7 @@ type: workplan title: "Client credentials and service-token issuance" domain: infotech repo: key-cape -status: active +status: finished owner: codex topic_slug: netkingdom created: "2026-07-27" @@ -79,7 +79,7 @@ module. ```task id: KEY-WP-0006-T04 -status: wait +status: done priority: high state_hub_task_id: "ba65d39f-63b8-42aa-87bb-99ec26821a8e" ``` @@ -89,13 +89,28 @@ 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 ```task id: KEY-WP-0006-T05 -status: wait +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.