key-cape/docs/native-authentication.md

88 lines
4.3 KiB
Markdown
Raw Normal View History

# Native caller authentication
KeyCape owns the `keycape service-token` and `keycape login` commands. They
produce issuer-verified JWTs in a new mode-0600 JSON file outside Git worktrees.
Existing files and symlinks are refused. Token responses and provider error
bodies are never printed. Commands exit nonzero on verification or delivery
failure and remove incomplete output files.
## Service exchange
Have the existing custody mechanism inject the registered client secret into a
named environment variable, then run:
```sh
keycape service-token --issuer https://kc.coulomb.social \
--client-id rapp-qonto --scope qonto:read \
--secret-env KEYCAPE_RAPP_QONTO_CLIENT_SECRET \
--out /private/runtime/qonto-token.json
```
The output directory must already exist. No secret belongs on argv. The command
uses client_secret_basic with OAuth form encoding. The server must include the
matching decoding fix for secrets containing reserved characters. The Qonto
client ID and scope above follow the deployed KEY-WP-0006 registration.
For approval-engine clients, set `--audience approval-engine` explicitly; the
expected audience otherwise defaults to the client ID. Both requested scopes
and audience must match the issuer's static registration. The output contains
`access_token`, `token_type`, and `expires_in`; a service exchange has no ID token.
Re-exchange before expiry for renewal. There is no refresh token or credential
cache. Client disablement stops new issuance; already-issued JWTs expire normally.
## Browser login
Use a registered **public PKCE** client with an exact literal-loopback callback:
```sh
keycape login --issuer https://kc.coulomb.social \
--client-id APPROVER_CLIENT_ID --audience approval-engine \
--scope 'openid approval:approve' \
--redirect-uri http://127.0.0.1:REGISTERED_PORT/callback \
--out /private/runtime/approver-token.json
```
Replace the client ID and port with the approved registration. This command
prints a browser authorization URL and waits up to five minutes. The provider
handles password and MFA; KeyCape CLI never receives those credentials. Configure
`mfaRequired: true` on the approver registration. State, nonce and S256 PKCE are
fresh for each attempt. The listener binds before the URL is printed, validates
callback host/path/state, and closes when the attempt ends.
Both tokens are verified against same-origin HTTPS discovery/JWKS. Access tokens
bind the expected resource audience and requested scopes. ID tokens bind the
client ID, nonce and matching subject. There is no insecure TLS switch and no
HTTP redirect following during credential exchange. Cross-origin discovery
endpoints and confidential browser clients are intentionally unsupported.
## OpenBao login boundary
The existing ops-warden login route runs
`bao login -no-print -method=oidc -path=netkingdom role=<domain>`. That produces an
**OpenBao token**, unlike KeyCape's new issuer JWT output. These commands are not
interchangeable. KeyCape owns browser authentication and issuer JWTs; OpenBao's
role mapping, token store and enforcement remain platform-owned. Do not replace
that route with `keycape login` without adapting and verifying its consumer
contract. No ops-warden route is retired by this change.
## Qonto rotation boundary
Native exchange is implemented. Rotation still needs a coordinated custody and
provider update, not a wrapper around a raw KV read. The reviewed sequence is:
1. Resolve exact existing authority for
`platform/workloads/rapp-qonto/keycape-client`, field `client_secret`, and
`sso/keycape-rapp-qonto-client`, key `client-secret`.
2. Generate the successor inside the approved custody execution transport;
retain the predecessor only there for bounded rollback and denial checks.
3. CAS-update that KV field while preserving siblings, update the matching
Kubernetes delivery reference, and restart KeyCape in the agreed window.
4. Verify the new `qonto:read` exchange, predecessor rejection and excess-scope
denial without exposing tokens; verify downstream readiness.
5. On failure, reconcile both custodians to the same version and reload KeyCape
before declaring rollback complete. Record versions and outcomes, never values.
No general rotation command is shipped until that cross-owner transaction has
an admitted execution and rollback contract. Calling secrets-engine's KV
rotation alone would leave the provider and consumers inconsistent.