Register coulomb-social OIDC client on live KeyCape (CSOC-WP-0002-T03)

Public PKCE client on kc.coulomb.social with local and production redirect
URIs. Add register-keycape-client.sh, document env, and harden public-client
token exchange (no secret). Authorize probe verified registered vs reject.
This commit is contained in:
tegwick 2026-08-09 01:50:51 +02:00
parent 179b20ceed
commit 76ec8cfe41
7 changed files with 138 additions and 30 deletions

21
.env.example Normal file
View file

@ -0,0 +1,21 @@
# Copy to .env for local overrides (never commit .env).
# python-decouple loads .env automatically when present.
SECRET_KEY=change-me
DEBUG=true
DATABASE_URL=sqlite:///db.sqlite3
DEFAULT_TENANT_ID=binky
# --- NetKingdom OIDC (KeyCape) ---
# Offline shell: leave OIDC_ENABLED=false and use /auth/dev-login/
OIDC_ENABLED=false
OIDC_ISSUER=https://kc.coulomb.social
OIDC_CLIENT_ID=coulomb-social
OIDC_REDIRECT_URI=http://127.0.0.1:8008/auth/callback/
# Public client — no secret:
# OIDC_CLIENT_SECRET=
OIDC_SCOPES=openid profile email groups
USER_ENGINE_APPLICATION_ID=coulomb-social
# USER_ENGINE_BASE_URL=
# FLEX_AUTH_BASE_URL=

View file

@ -16,7 +16,7 @@
| task | CSOC-WP-0001-T04 | todo | — | workplans/CSOC-WP-0001-bubble-io-exit-assessment.md |
| task | CSOC-WP-0002-T01 | done | — | workplans/CSOC-WP-0002-netkingdom-user-management-reestablish.md |
| task | CSOC-WP-0002-T02 | done | — | workplans/CSOC-WP-0002-netkingdom-user-management-reestablish.md |
| task | CSOC-WP-0002-T03 | todo | — | workplans/CSOC-WP-0002-netkingdom-user-management-reestablish.md |
| task | CSOC-WP-0002-T03 | done | — | workplans/CSOC-WP-0002-netkingdom-user-management-reestablish.md |
| task | CSOC-WP-0002-T04 | progress | — | workplans/CSOC-WP-0002-netkingdom-user-management-reestablish.md |
| task | CSOC-WP-0002-T05 | done | — | workplans/CSOC-WP-0002-netkingdom-user-management-reestablish.md |
| task | CSOC-WP-0002-T06 | done | — | workplans/CSOC-WP-0002-netkingdom-user-management-reestablish.md |

View file

@ -35,18 +35,29 @@ def discovery_document() -> dict[str, Any]:
return resp.json()
def _oauth_client() -> OAuth2Client:
"""Public clients (KeyCape default for browser apps) use PKCE without a secret."""
secret = (settings.OIDC_CLIENT_SECRET or "").strip() or None
kwargs: dict[str, Any] = {
"client_id": settings.OIDC_CLIENT_ID,
"redirect_uri": settings.OIDC_REDIRECT_URI,
"scope": settings.OIDC_SCOPES,
"code_challenge_method": "S256",
}
if secret:
kwargs["client_secret"] = secret
else:
# Authlib: omit secret for public clients
kwargs["token_endpoint_auth_method"] = "none"
return OAuth2Client(**kwargs)
def build_authorization_url(*, state: str, code_verifier: str) -> str:
if not oidc_configured():
raise OIDCConfigurationError("OIDC is not enabled/configured")
doc = discovery_document()
auth_endpoint = doc["authorization_endpoint"]
client = OAuth2Client(
client_id=settings.OIDC_CLIENT_ID,
client_secret=settings.OIDC_CLIENT_SECRET or None,
redirect_uri=settings.OIDC_REDIRECT_URI,
scope=settings.OIDC_SCOPES,
code_challenge_method="S256",
)
client = _oauth_client()
uri, _ = client.create_authorization_url(
auth_endpoint,
state=state,
@ -58,11 +69,7 @@ def build_authorization_url(*, state: str, code_verifier: str) -> str:
def exchange_code(code: str, *, code_verifier: str) -> dict[str, Any]:
doc = discovery_document()
token_endpoint = doc["token_endpoint"]
client = OAuth2Client(
client_id=settings.OIDC_CLIENT_ID,
client_secret=settings.OIDC_CLIENT_SECRET or None,
redirect_uri=settings.OIDC_REDIRECT_URI,
)
client = _oauth_client()
token = client.fetch_token(
token_endpoint,
code=code,

View file

@ -27,17 +27,41 @@ uv run pytest
## Environment
See `.env.example`. Summary:
| Variable | Default | Purpose |
|----------|---------|---------|
| `SECRET_KEY` | insecure dev default | Django secret |
| `DATABASE_URL` | sqlite `./db.sqlite3` | DB |
| `DEFAULT_TENANT_ID` | `binky` | Client #1 tenant key |
| `OIDC_ENABLED` | `false` | Use real NetKingdom issuer |
| `OIDC_ISSUER` | | Issuer base URL |
| `OIDC_CLIENT_ID` / `OIDC_CLIENT_SECRET` | | OIDC client (secret from OpenBao in prod) |
| `OIDC_REDIRECT_URI` | | e.g. `http://127.0.0.1:8008/auth/callback/` |
| `OIDC_ENABLED` | `false` | Use KeyCape / real issuer |
| `OIDC_ISSUER` | | e.g. `https://kc.coulomb.social` |
| `OIDC_CLIENT_ID` | | `coulomb-social` |
| `OIDC_CLIENT_SECRET` | empty | **public client** — leave empty |
| `OIDC_REDIRECT_URI` | | `http://127.0.0.1:8008/auth/callback/` |
| `USER_ENGINE_BASE_URL` | empty (stub) | user-engine HTTP |
| `USER_ENGINE_APPLICATION_ID` | `coulomb-social` | App id in user-engine |
| `FLEX_AUTH_BASE_URL` | empty (fail-closed except shell:view) | PDP |
### Platform OIDC (KeyCape)
Client is registered on railiance01 KeyCape. Re-apply if redirect URIs change:
```bash
./scripts/register-keycape-client.sh
```
Then:
```bash
export OIDC_ENABLED=true
export OIDC_ISSUER=https://kc.coulomb.social
export OIDC_CLIENT_ID=coulomb-social
export OIDC_REDIRECT_URI=http://127.0.0.1:8008/auth/callback/
export OIDC_SCOPES="openid profile email groups"
make run
```
Sign-in redirects to Authelia (`auth.coulomb.social`) + MFA via privacyIDEA.
See `docs/adr/ADR-0001-netkingdom-identity.md` and `docs/identity/`.

View file

@ -5,21 +5,45 @@ Non-secret registration checklist for NetKingdom IAM Profile issuers.
| Field | Value / notes |
|-------|----------------|
| Application | coulomb.social |
| Profile | NetKingdom IAM Profile (PKCE required) |
| client_id | set per environment (`OIDC_CLIENT_ID`) |
| client_secret | OpenBao / local 0600 only — never Git |
| redirect_uri (dev) | `http://127.0.0.1:8008/auth/callback/` |
| redirect_uri (prod) | `https://coulomb.social/auth/callback/` (confirm at deploy) |
| scopes | `openid profile email` (+ profile claims as issuer provides) |
| grant | authorization_code + PKCE S256 |
| wildcard redirects | **forbidden** (KeyCape constraint) |
| Profile | NetKingdom IAM Profile (PKCE S256 required) |
| Issuer (live) | `https://kc.coulomb.social` (KeyCape lightweight mode) |
| client_id | `coulomb-social` |
| client_type | **public** (no client secret; PKCE only) |
| redirect_uri (dev) | `http://127.0.0.1:8008/auth/callback/` · `http://localhost:8008/auth/callback/` |
| redirect_uri (prod) | `https://coulomb.social/auth/callback/` |
| scopes | `openid profile email groups` |
| grant | authorization_code |
| wildcard redirects | **forbidden** |
## Status
| Environment | Issuer mode | Registered |
|-------------|-------------|------------|
| local | dev claims (`OIDC_ENABLED=false`) | n/a |
| platform | KeyCape / local-identity / Keycloak | pending operator registration (T03) |
| local offline | dev claims (`OIDC_ENABLED=false`) | n/a |
| platform (railiance01) | KeyCape @ `kc.coulomb.social` | **yes** (2026-08-09) |
| key-cape dev-config | local compose | yes (repo `config/dev-config.yaml`) |
When registering, record the live client_id here (not the secret) and the
issuer base URL in deploy notes.
### Live verification (2026-08-09)
- Registered via `scripts/register-keycape-client.sh`
`net-kingdom/sso-mfa/k8s/keycape/register-coulomb-social.sh` (patches
`sso/keycape-config`, rolls out KeyCape).
- `GET /authorize` with registered redirect → **200**, browser handoff to Authelia.
- Unregistered redirect → **400** `invalid_profile_usage` / `redirect_uri does not match`.
### Enable in the app
```bash
export OIDC_ENABLED=true
export OIDC_ISSUER=https://kc.coulomb.social
export OIDC_CLIENT_ID=coulomb-social
export OIDC_REDIRECT_URI=http://127.0.0.1:8008/auth/callback/
# leave OIDC_CLIENT_SECRET unset (public client)
make run
```
Re-register after redirect URI changes:
```bash
./scripts/register-keycape-client.sh
```

View file

@ -0,0 +1,26 @@
#!/usr/bin/env bash
# Register this app's OIDC client on live KeyCape (kc.coulomb.social).
# Delegates to net-kingdom/sso-mfa/k8s/keycape/register-coulomb-social.sh
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
NK_SCRIPT="${NET_KINGDOM_KEYCAPE_DIR:-$HOME/net-kingdom/sso-mfa/k8s/keycape}/register-coulomb-social.sh"
if [[ ! -x "$NK_SCRIPT" && ! -f "$NK_SCRIPT" ]]; then
echo "ERROR: KeyCape register script not found: $NK_SCRIPT" >&2
echo "Clone net-kingdom or set NET_KINGDOM_KEYCAPE_DIR." >&2
exit 1
fi
chmod +x "$NK_SCRIPT" 2>/dev/null || true
bash "$NK_SCRIPT"
echo ""
echo "Next: export OIDC settings and restart the app:"
echo " export OIDC_ENABLED=true"
echo " export OIDC_ISSUER=https://kc.coulomb.social"
echo " export OIDC_CLIENT_ID=coulomb-social"
echo " export OIDC_REDIRECT_URI=http://127.0.0.1:8008/auth/callback/"
echo " # no client secret — public + PKCE"
echo " make run"
echo "Docs: $ROOT/docs/identity/oidc-client.md"

View file

@ -126,7 +126,7 @@ migrations; no product domain models beyond auth shell.
```task
id: CSOC-WP-0002-T03
status: todo
status: done
priority: high
state_hub_task_id: "1b1d1162-d44a-45db-b906-05ebd7110f21"
```
@ -144,6 +144,12 @@ Record non-secret binding facts under `docs/identity/oidc-client.md`.
**Done when:** browser authorization code + PKCE login completes against the
chosen issuer in a dev environment; evidence in `docs/identity/`.
2026-08-09: Registered public client `coulomb-social` on live KeyCape
(`kc.coulomb.social`) via `scripts/register-keycape-client.sh`. Authorize
probe: registered redirect → Authelia handoff; bad redirect →
`invalid_profile_usage`. Human MFA browser login remains the final UX check
when running the app with `OIDC_ENABLED=true`.
## T04 — user-engine application onboarding
```task