Operator decision 5ed3fb35-eca9-413a-82b9-95171ba85bf6 accepts tenant:platform
as the platform management tenant for the Glas approval chain, requiring exact
spelling across the approval store, the service-client JWT claim and the
lifecycle CheckRequest.
Changes the tenant field on secrets-engine-approval and approval-engine-operator
only, in the registration fixture and the provisioning packet. Unrelated clients
and the human directory default keep tenant:coulomb, and no audience, scope,
subject, role, lifetime or MFA grant changes.
Adds issuance evidence that the approval shape emits tenant:platform exactly and
never an alias the caller requests, that the OpenBao client gains no
cross-tenant reach, and a fixture guard pinning every reviewed client's tenant.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NV9oijZukGyGbRQGGKnK4P
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 713576@bnt-lap001
Assistant-Session: 384c511d-9bce-4cb8-a676-2aef6c0c8df6
Answers the GLAS-WP-0015 tenant-alignment request. The JWT tenant is
tenant:coulomb for all four reviewed service registrations; the approval store
tenant and the policy tenant belong to other owners and KeyCape does no
normalization between them. No mapping is invented and no live registration
changes.
Adds regression tests proving the tenant claim is bound at registration and is
not influenced by request parameters, that distinct registrations never carry
each other's tenant, and that human tokens fall back to the platform tenant
rather than an empty claim.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NV9oijZukGyGbRQGGKnK4P
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 713576@bnt-lap001
Assistant-Session: 384c511d-9bce-4cb8-a676-2aef6c0c8df6
Add signed registration/enrollment handoffs, per-request assurance
policy with login-session isolation, and /logout. coulomb-social
stays AAL1 unless acr_values or another client raises the bar.
Allow coulomb-social ordinary login at AAL1 via mfaRequired: false while
keeping provider requireForAll for clients without an override. Preserve
explicit acr_values=aal2 for step-up.
New internal/adapters/tenantengine package, mirroring
internal/adapters/{lldap,privacyidea,authelia}'s shape: Client.Roles()
calls tenant-engine's cache-read endpoint. Fails open by construction --
unreachable, non-200, malformed body, or a nil *Client all return
(nil, false), never an error to specially handle. Wired into
TokenHandler.TenantEngine (nil by default, existing tests unaffected);
token.go stamps tenant_roles only when ok.
7 adapter tests plus 3 TokenHandler-level tests proving the actual
required behavior end-to-end: present when reachable, token issuance still
200 with every other core claim intact when unreachable (tenant_roles
simply absent -- the literal done-criteria), absent when not configured.
Real bug found and fixed at the source, not worked around: the first live
cross-process check (real flex-auth, real tenant-engine, this adapter)
returned tenant_not_found for a tenant that existed -- tenant-engine's read
endpoint was keyed by its internal tenant_id, but key-cape only ever has
the tenant's profile identifier. Fixed in tenant-engine
(ADHOC-2026-07-24), re-verified with the same live three-process chain --
roles=[IAM] ok=true.
Workplan closed: T01-T03 done. Explicitly still open: client_credentials /
service-token issuance -- no such flow exists in token.go at all, a
materially larger separate piece of work than either task here.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Verified first: grant_types_supported advertises client_credentials in
discovery.go, but token.go only ever accepted authorization_code -- no
service-token issuance path exists at all. Building one from scratch is
materially bigger than extending the existing flow; explicitly not
attempted here, left open in the workplan rather than declared done.
What shipped for the human Authorization Code + PKCE flow:
- domain.User.Tenant (new, omitempty) + token.go's effectiveTenant():
falls back to tenant:coulomb (this workstation's actual tenant, ADR-0006)
when unset -- never an empty tenant claim, never a silent reassignment.
- principal_type: "human", unconditional.
- groups/roles promoted from scope-gated to unconditional core claims,
always [] not null when empty. One pre-existing test asserted the old
scope-gated groups behavior -- updated to match the new intentional
behavior, not left failing or reverted.
- assurance built from PKCESession.MFAVerified (new field, threaded
through completeAuthorization's two call sites in authorize.go) --
whether MFA was actually verified in this session, not static enrollment
state. aal2 only when required-and-passed this time, aal1 otherwise.
go build/vet clean, go test ./... green repo-wide. Two new authorize_test.go
cases assert MFAVerified on both paths. tests/profile/profile_test.go's
TestCompleteTokenFlow (the repo's own full HTTP integration test) extended
with real value assertions for all five claims, not just presence checks.
Python conformance tool not run against a live instance (needs the full
Authelia+LLDAP+privacyIDEA stack); TestCompleteTokenFlow's real HTTP round
trip covers the equivalent claim checks instead.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The adapter was forwarding the downstream client's client_id and
redirect_uri to Authelia, which would always be rejected — Authelia
only recognises client_id=keycape and its registered callback URI.
Also removed downstream PKCE forwarding: KeyCape is a confidential
OIDC client to Authelia and authenticates via client_secret instead.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- T22: docker-compose.dev.yml dev stack, Dockerfile, root Makefile
- T18: Profile test suite (Scenario A) — 8 integration tests with real handlers
- T23: Server binary wiring all components, config validation, /healthz
- Config: ValidateConfig with startup validation
14 test packages pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- T09: /userinfo with RS256 JWT validation, scope-filtered claims
- T15: LLDAP→canonical export tool with validation, migration_event telemetry
- T21: Negative test suite (Scenario D) — all 7 unsupported features verified
All go tests passing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>