Refuse to invent the human approver client_id and callback URI
key-cape (KEY-WP-0013-T02) asked for two exact strings for the human approver registration. This engine is a bearer-token resource server: no redirect endpoint, no authorization-code/PKCE path, and no Ingress or external origin, so neither string exists here. Record why, name the missing owner (a browser-facing approver UI outside this repo), and confirm that the access token — never the ID token — is what this resource validates. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HyybaE7DUXrWYrhbnESCTe Assistant: claude-code Assistant-Model: opus Assistant-Process: 1275879@bnt-lap001 Assistant-Session: eb464208-f821-41b2-bc5a-a6c33d92a8ad
This commit is contained in:
parent
d5d1e41035
commit
24ec18fe3f
2 changed files with 56 additions and 0 deletions
|
|
@ -117,3 +117,43 @@ clients:
|
||||||
Human approvers use the existing KeyCape human flow with `approval:approve`
|
Human approvers use the existing KeyCape human flow with `approval:approve`
|
||||||
only, still with `aud=approval-engine`. They must not receive
|
only, still with `aud=approval-engine`. They must not receive
|
||||||
`approval:consume`.
|
`approval:consume`.
|
||||||
|
|
||||||
|
## Human approver client — not supplied (2026-09-08)
|
||||||
|
|
||||||
|
`KEY-WP-0013-T02` asked for two exact strings for the human approver
|
||||||
|
registration (public client, authorization code + S256 PKCE, exact redirect,
|
||||||
|
`aud=approval-engine`, `allowedScopes: [openid, approval:approve]`,
|
||||||
|
`mfaRequired: true`): the `client_id` and the full callback URI.
|
||||||
|
|
||||||
|
**This repository cannot supply either, and must not guess one.** Redirects are
|
||||||
|
matched exactly at `/authorize`, so a fabricated value fails closed — or, worse,
|
||||||
|
registers a redirect no deployed component owns.
|
||||||
|
|
||||||
|
Why there is nothing to give:
|
||||||
|
|
||||||
|
- approval-engine is a bearer-token **resource server** only. `approval_engine/api.py`
|
||||||
|
is a WSGI app whose every non-health route reads `Authorization: Bearer …`
|
||||||
|
(`approval_engine/auth.py`). There is no `/callback`, no redirect handling, no
|
||||||
|
authorization-code or PKCE code anywhere in the package.
|
||||||
|
- Nothing here is a browser client. The requested registrations above are both
|
||||||
|
`client_credentials` confidential service clients.
|
||||||
|
- The service is not publicly addressable: `deploy/approval-engine.yaml` has a
|
||||||
|
Namespace, ClusterIP Service and StatefulSet — no Ingress, no external
|
||||||
|
hostname — so no `https://…/callback` origin exists to register.
|
||||||
|
|
||||||
|
The human approver flow therefore belongs to whichever **browser-facing
|
||||||
|
approver UI** presents `approval:approve` tokens to this engine. That component
|
||||||
|
is not in this repo and is not named in `APPROVAL-WP-0002`. The two strings must
|
||||||
|
come from its owner once it exists and has a deployed origin; approval-engine
|
||||||
|
only needs the resulting access token to carry `aud=approval-engine`,
|
||||||
|
`principal_type: human`, `tenant: tenant:platform` and scope `approval:approve`.
|
||||||
|
|
||||||
|
### Token confirmed: access token, never the ID token
|
||||||
|
|
||||||
|
`ApiApplication.identity` verifies the **access token** presented as
|
||||||
|
`Authorization: Bearer` at this resource — RS256 against the deployed issuer's
|
||||||
|
`/jwks`, checking `iss`, `aud=approval-engine`, `exp`/`iat` (30s leeway), `sub`,
|
||||||
|
`principal_type`, `tenant` (exact equality with the store tenant), `roles`,
|
||||||
|
`scope` and `assurance`. `id_token` appears nowhere in the codebase; the ID
|
||||||
|
token belongs to the login client and is never evidence here. Human principals
|
||||||
|
are additionally refused `approval:consume`.
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,22 @@ This resolves the choice of value only. T01 stays `progress`: KeyCape still has
|
||||||
to own and prove these registrations, and credential materialization is
|
to own and prove these registrations, and credential materialization is
|
||||||
unchanged.
|
unchanged.
|
||||||
|
|
||||||
|
2026-09-08 follow-up: key-cape (`KEY-WP-0013-T02`) asked for the human approver
|
||||||
|
client's exact `client_id` and full callback URI. Neither is supplied, and
|
||||||
|
neither is guessed: this engine is a bearer-token resource server with no
|
||||||
|
redirect endpoint, no authorization-code/PKCE code and no Ingress or external
|
||||||
|
origin (`deploy/approval-engine.yaml` is Namespace + ClusterIP + StatefulSet),
|
||||||
|
so no callback URI exists here to register — and redirects match exactly at
|
||||||
|
`/authorize`, so an invented value either fails closed or registers an origin no
|
||||||
|
component owns. The human browser client belongs to an approver UI that this
|
||||||
|
repo does not contain and `APPROVAL-WP-0002` does not name; the two strings must
|
||||||
|
come from that component's owner. Confirmed to key-cape in the same reply: this
|
||||||
|
resource validates the **access** token (`Authorization: Bearer`, RS256 via the
|
||||||
|
issuer's `/jwks`, checking `iss`, `aud=approval-engine`, expiry, `sub`,
|
||||||
|
`principal_type`, `tenant`, `roles`, `scope`, `assurance`); `id_token` appears
|
||||||
|
nowhere in the codebase. Recorded in `docs/keycape-service-registrations.md`.
|
||||||
|
T01 stays `progress`.
|
||||||
|
|
||||||
## Harden durable storage and migrations
|
## Harden durable storage and migrations
|
||||||
|
|
||||||
```task
|
```task
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue