generated from coulomb/repo-seed
bootrapping support
Some checks failed
Build and Publish Container Image / build-and-push (push) Has been cancelled
Some checks failed
Build and Publish Container Image / build-and-push (push) Has been cancelled
This commit is contained in:
194
workplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md
Normal file
194
workplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md
Normal file
@@ -0,0 +1,194 @@
|
||||
---
|
||||
id: KEY-WP-0003
|
||||
type: workplan
|
||||
title: "Bootstrap Console OIDC Login and MFA Verification"
|
||||
domain: netkingdom
|
||||
repo: key-cape
|
||||
status: finished
|
||||
owner: codex
|
||||
topic_slug: netkingdom
|
||||
created: "2026-05-24"
|
||||
updated: "2026-05-24"
|
||||
---
|
||||
|
||||
# KEY-WP-0003 - Bootstrap Console OIDC Login and MFA Verification
|
||||
|
||||
## Problem
|
||||
|
||||
The NetKingdom security bootstrap console now acts as a local OIDC client
|
||||
callback so the operator can verify the dedicated platform-root login before
|
||||
approving custody mode. The current live KeyCape deployment rejects that flow
|
||||
with:
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "invalid_profile_usage",
|
||||
"description": "redirect_uri does not match any registered URI",
|
||||
"feature": "redirect_uri"
|
||||
}
|
||||
```
|
||||
|
||||
That error is correct profile enforcement: KeyCape only accepts exact
|
||||
registered redirect URIs. The live `demo-app` registration has not yet been
|
||||
updated to allow the local bootstrap console callback:
|
||||
|
||||
- `http://127.0.0.1:8876/oidc/callback`
|
||||
- `http://localhost:8876/oidc/callback`
|
||||
|
||||
After that is fixed, there is a second usability/security gap. KeyCape checks
|
||||
privacyIDEA MFA after the Authelia callback, but the browser flow currently
|
||||
expects an `mfa_token` query parameter instead of presenting a proper OTP
|
||||
challenge page to the human operator.
|
||||
|
||||
## Goal
|
||||
|
||||
Make the bootstrap console's "Start demo OIDC login" button a real
|
||||
end-to-end verification path for the current lightweight IAM stack:
|
||||
|
||||
1. KeyCape accepts the bootstrap console callback URI by exact registration.
|
||||
2. The browser leaves KeyCape for the public Authelia login URL.
|
||||
3. After password login, KeyCape presents a minimal MFA challenge when
|
||||
privacyIDEA requires one.
|
||||
4. KeyCape issues an OIDC authorization code to the bootstrap console callback.
|
||||
5. The console can exchange the code and let the operator mark
|
||||
`OIDC login verified` without exposing tokens or secrets.
|
||||
|
||||
This keeps KeyCape's security posture intact: no wildcard redirect URIs, no
|
||||
dynamic client registration, no token display, and no storage of OTP material.
|
||||
|
||||
## Design Notes
|
||||
|
||||
- Prefer a dedicated public client named `netkingdom-bootstrap-console` for
|
||||
long-lived clarity. Reusing `demo-app` is acceptable for the immediate
|
||||
unblock only if the deployment/runbook clearly labels it as a bootstrap test
|
||||
client.
|
||||
- The bootstrap callback is local-only and operator-attended. It must be an
|
||||
exact URI in config, not a wildcard or dynamic registration exception.
|
||||
- Browser-facing Authelia redirects must use the public Authelia base URL
|
||||
(`https://auth.coulomb.social`) so the human login page opens correctly.
|
||||
- KeyCape may still need an internal service URL for back-channel token
|
||||
exchange. If so, split the current single Authelia URL into browser-facing
|
||||
authorize URL and internal token URL instead of making the browser use an
|
||||
in-cluster hostname.
|
||||
- The MFA prompt should collect only a one-time code, post it back to KeyCape,
|
||||
validate with privacyIDEA, and then continue the normal OIDC code flow.
|
||||
- This work unblocks the NetKingdom custody gate in
|
||||
`NET-WP-0015-platform-root-custody-and-openbao-identity-bootstrap`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
**2026-05-24:** Implemented in source:
|
||||
|
||||
- added `netkingdom-bootstrap-console` as a public OIDC client in the sample
|
||||
KeyCape config, while keeping the local callback registered on `demo-app`
|
||||
for compatibility,
|
||||
- split Authelia browser redirects from server-side token exchange via
|
||||
`browserBaseURL` and `tokenBaseURL`,
|
||||
- added a browser MFA challenge page at `POST /authorize/callback` that
|
||||
validates the one-time code with privacyIDEA before issuing the downstream
|
||||
OIDC authorization code,
|
||||
- updated NetKingdom's `keycape-config` generation template and bootstrap
|
||||
console to use the dedicated client,
|
||||
- added regression tests for callback registration, split Authelia URLs, MFA
|
||||
challenge rendering, valid OTP continuation, and invalid OTP failure.
|
||||
|
||||
Live use still requires deployment: build/publish the updated KeyCape image,
|
||||
refresh the live `keycape-config` Secret through the custodian age-key unlock
|
||||
ceremony, and restart the KeyCape deployment.
|
||||
|
||||
---
|
||||
|
||||
## T01 - Register the bootstrap console callback client
|
||||
|
||||
```task
|
||||
id: KEY-WP-0003-T01
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
Add a KeyCape client registration for the bootstrap console. Either create a
|
||||
dedicated `netkingdom-bootstrap-console` public client or update `demo-app`
|
||||
temporarily with these exact redirect URIs:
|
||||
|
||||
- `http://127.0.0.1:8876/oidc/callback`
|
||||
- `http://localhost:8876/oidc/callback`
|
||||
|
||||
Update the sample config, tests, and deployment/runbook references so the
|
||||
registered client is reproducible and not just a live-cluster patch.
|
||||
|
||||
Gate: an authorize request using the local callback no longer returns
|
||||
`invalid_profile_usage` for `redirect_uri`.
|
||||
|
||||
## T02 - Separate browser-facing and internal Authelia URLs if needed
|
||||
|
||||
```task
|
||||
id: KEY-WP-0003-T02
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
Confirm whether the current `authelia.baseURL` is safe to use for both browser
|
||||
redirects and server-side token exchange. If not, add explicit configuration
|
||||
for the browser authorize base URL and internal token/userinfo base URL.
|
||||
|
||||
Gate: the first browser redirect leaves `https://kc.coulomb.social` for
|
||||
`https://auth.coulomb.social/...`; server-side token exchange still works from
|
||||
inside the deployment.
|
||||
|
||||
## T03 - Add a browser MFA challenge step
|
||||
|
||||
```task
|
||||
id: KEY-WP-0003-T03
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
When `CheckMFARequired` returns true after the Authelia callback, render a
|
||||
minimal KeyCape MFA challenge page instead of requiring `mfa_token` in the
|
||||
callback query string. The page should:
|
||||
|
||||
- show the authenticated username and client display name,
|
||||
- collect only the OTP code,
|
||||
- preserve the pending OIDC state server-side,
|
||||
- validate with privacyIDEA,
|
||||
- continue to issue the normal authorization code on success,
|
||||
- fail closed with the existing telemetry on invalid MFA.
|
||||
|
||||
Gate: a user enrolled in privacyIDEA can complete password + OTP in the
|
||||
browser and is returned to the registered downstream callback.
|
||||
|
||||
## T04 - Add end-to-end profile tests for the bootstrap login path
|
||||
|
||||
```task
|
||||
id: KEY-WP-0003-T04
|
||||
status: done
|
||||
priority: medium
|
||||
```
|
||||
|
||||
Add tests that cover:
|
||||
|
||||
- local bootstrap callback registration,
|
||||
- rejection of unregistered callbacks remains intact,
|
||||
- Authelia browser redirect uses the expected public URL,
|
||||
- MFA-required login presents a challenge instead of immediate failure,
|
||||
- invalid OTP fails closed,
|
||||
- valid OTP produces an authorization code bound to the original PKCE session.
|
||||
|
||||
Gate: `make test` passes and the negative redirect URI tests remain green.
|
||||
|
||||
## T05 - Document the live rollout ceremony
|
||||
|
||||
```task
|
||||
id: KEY-WP-0003-T05
|
||||
status: done
|
||||
priority: medium
|
||||
```
|
||||
|
||||
Document the deployment path for updating live KeyCape config without
|
||||
regenerating unrelated secrets. The runbook must fit the NetKingdom custodian
|
||||
age-key model: decrypt or unlock only during an attended ceremony, apply the
|
||||
updated client registration/config, restart KeyCape, and remove plaintext
|
||||
secret material afterward.
|
||||
|
||||
Gate: an operator can update the live `keycape-config` Secret and verify the
|
||||
bootstrap console OIDC login without printing or committing secrets.
|
||||
Reference in New Issue
Block a user