From 76ec8cfe41ceecc9ad12acb18efc07a2154abb48 Mon Sep 17 00:00:00 2001 From: tegwick Date: Sun, 9 Aug 2026 01:50:51 +0200 Subject: [PATCH] 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. --- .env.example | 21 ++++++++ WORK-RECORDS.md | 2 +- coulomb_social/apps/identity/oidc.py | 31 +++++++----- docs/dev.md | 32 +++++++++++-- docs/identity/oidc-client.md | 48 ++++++++++++++----- scripts/register-keycape-client.sh | 26 ++++++++++ ...-netkingdom-user-management-reestablish.md | 8 +++- 7 files changed, 138 insertions(+), 30 deletions(-) create mode 100644 .env.example create mode 100755 scripts/register-keycape-client.sh diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..ecf1d51 --- /dev/null +++ b/.env.example @@ -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= diff --git a/WORK-RECORDS.md b/WORK-RECORDS.md index ca152a6..5e07e2e 100644 --- a/WORK-RECORDS.md +++ b/WORK-RECORDS.md @@ -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 | diff --git a/coulomb_social/apps/identity/oidc.py b/coulomb_social/apps/identity/oidc.py index 1d30224..90ad98a 100644 --- a/coulomb_social/apps/identity/oidc.py +++ b/coulomb_social/apps/identity/oidc.py @@ -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, diff --git a/docs/dev.md b/docs/dev.md index 5e35ae5..9601c8c 100644 --- a/docs/dev.md +++ b/docs/dev.md @@ -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/`. diff --git a/docs/identity/oidc-client.md b/docs/identity/oidc-client.md index 9e9efb6..2dedd7f 100644 --- a/docs/identity/oidc-client.md +++ b/docs/identity/oidc-client.md @@ -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 +``` diff --git a/scripts/register-keycape-client.sh b/scripts/register-keycape-client.sh new file mode 100755 index 0000000..751e557 --- /dev/null +++ b/scripts/register-keycape-client.sh @@ -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" diff --git a/workplans/CSOC-WP-0002-netkingdom-user-management-reestablish.md b/workplans/CSOC-WP-0002-netkingdom-user-management-reestablish.md index 5a7570a..b4b1778 100644 --- a/workplans/CSOC-WP-0002-netkingdom-user-management-reestablish.md +++ b/workplans/CSOC-WP-0002-netkingdom-user-management-reestablish.md @@ -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