fix(oidc): register SPA clients from env-driven startup upsert (BUNYIP-57) #75

Merged
nrupard merged 2 commits from fix/bunyip-57-env-driven-oidc-client-seed into main 2026-06-08 19:24:27 +02:00
Owner

Problem

The static migration 20260603000010_register_mokosh_apps_and_drillmark_oidc_clients.sql seeds the mokosh-apps and drillmark OIDC clients with hardcoded staging (a8n.systems) redirect_uris, post_logout_redirect_uris, and audience. sqlx::migrate! runs that SQL verbatim on every database regardless of ENVIRONMENT, so on the nc-01 production deployment (psa.systems) the authorize handler's exact-string redirect_uri match (crates/bunyip-oidc/src/handlers/oidc.rs) fails and production PKCE login breaks before any token is minted. The minted at+jwt also carries the staging audience, which mokosh-server rejects against its OIDC_AUDIENCE. This blocks DEV-362.

Change

Move per-environment client registration out of the env-blind migration into an idempotent, env-driven startup upsert in bunyip-api/src/main.rs, mirroring the existing SETUP_DEFAULT_ADMIN seed block. After migrations, for each SPA client read per-client env vars (MOKOSH_APPS_*, DRILLMARK_*) via secret_env (so the {NAME}_FILE compose-secret convention works and empty counts as unset) and upsert keyed on the fixed client_id UUID. Only redirect_uris, post_logout_redirect_uris, and audience are written; all other columns keep their migration-defined values via ON CONFLICT (client_id) DO UPDATE of just those three columns. Startup is therefore authoritative and self-healing: it corrects the stale staging row in place.

  • Env-gated: a client whose vars are unset is skipped and logged (not registered), so drillmark stays unregistered on nc-01 until its vars are set, while mokosh-apps is corrected.
  • Comma-separated: *_REDIRECT_URIS / *_POST_LOGOUT_REDIRECT_URIS are split on commas because the columns are TEXT[].
  • Migration 20260603000010 stays as-is; its ON CONFLICT DO NOTHING is harmless once the startup upsert is authoritative.

Follow-up (DEV-362)

Set the MOKOSH_APPS_* (and eventually DRILLMARK_*) values in each env's compose-variables.yml: psa.systems values for nc-01, a8n.systems values for c-01 (no churn on staging).

Verification

just check-container green: cargo fmt --check, cargo clippy --workspace --all-targets -D warnings, cargo test --workspace --lib all pass.

Closes BUNYIP-57.

## Problem The static migration `20260603000010_register_mokosh_apps_and_drillmark_oidc_clients.sql` seeds the `mokosh-apps` and `drillmark` OIDC clients with hardcoded **staging** (`a8n.systems`) `redirect_uris`, `post_logout_redirect_uris`, and `audience`. `sqlx::migrate!` runs that SQL verbatim on every database regardless of `ENVIRONMENT`, so on the nc-01 production deployment (`psa.systems`) the authorize handler's exact-string `redirect_uri` match (`crates/bunyip-oidc/src/handlers/oidc.rs`) fails and production PKCE login breaks before any token is minted. The minted `at+jwt` also carries the staging `audience`, which mokosh-server rejects against its `OIDC_AUDIENCE`. This blocks DEV-362. ## Change Move per-environment client registration out of the env-blind migration into an idempotent, env-driven startup upsert in `bunyip-api/src/main.rs`, mirroring the existing `SETUP_DEFAULT_ADMIN` seed block. After migrations, for each SPA client read per-client env vars (`MOKOSH_APPS_*`, `DRILLMARK_*`) via `secret_env` (so the `{NAME}_FILE` compose-secret convention works and empty counts as unset) and upsert keyed on the fixed `client_id` UUID. Only `redirect_uris`, `post_logout_redirect_uris`, and `audience` are written; all other columns keep their migration-defined values via `ON CONFLICT (client_id) DO UPDATE` of just those three columns. Startup is therefore authoritative and self-healing: it corrects the stale staging row in place. - **Env-gated**: a client whose vars are unset is skipped and logged (not registered), so `drillmark` stays unregistered on nc-01 until its vars are set, while `mokosh-apps` is corrected. - **Comma-separated**: `*_REDIRECT_URIS` / `*_POST_LOGOUT_REDIRECT_URIS` are split on commas because the columns are `TEXT[]`. - Migration `20260603000010` stays as-is; its `ON CONFLICT DO NOTHING` is harmless once the startup upsert is authoritative. ## Follow-up (DEV-362) Set the `MOKOSH_APPS_*` (and eventually `DRILLMARK_*`) values in each env's `compose-variables.yml`: `psa.systems` values for nc-01, `a8n.systems` values for c-01 (no churn on staging). ## Verification `just check-container` green: `cargo fmt --check`, `cargo clippy --workspace --all-targets -D warnings`, `cargo test --workspace --lib` all pass. Closes BUNYIP-57.
fix(oidc): register SPA clients from env-driven startup upsert (BUNYIP-57)
All checks were successful
Check / fmt / clippy / build / test (pull_request) Successful in 1m15s
8608d18070
The static migration 20260603000010 seeds the mokosh-apps and drillmark OIDC clients with hardcoded staging (a8n.systems) redirect_uris, post_logout_redirect_uris, and audience. sqlx::migrate! runs that SQL verbatim on every database regardless of ENVIRONMENT, so the authorize handler's exact-string redirect_uri match fails on psa.systems and production PKCE login breaks before any token is minted; the minted at+jwt also carries the wrong audience for the RS to verify.

Move per-environment client registration out of the env-blind migration into an idempotent, env-driven startup upsert in main.rs, mirroring the SETUP_DEFAULT_ADMIN seed block. After migrations, for each SPA client read per-client env vars (MOKOSH_APPS_* and DRILLMARK_*, via secret_env so the {NAME}_FILE secret convention works and empty counts as unset) and upsert keyed on the fixed client_id UUID. Only redirect_uris, post_logout_redirect_uris, and audience are written; all other columns keep their migration-defined values via ON CONFLICT DO UPDATE of just those three columns. This makes startup authoritative and self-healing: it corrects the stale staging row in place.

Each client is env-gated: with its vars unset the upsert is skipped and logged, so drillmark stays unregistered on nc-01 (not yet deployed) while mokosh-apps is corrected. The *_REDIRECT_URIS / *_POST_LOGOUT_REDIRECT_URIS vars are comma-separated since the columns are TEXT[]. Migration 20260603000010 stays as-is; its ON CONFLICT DO NOTHING is harmless once the startup upsert is authoritative.

Companion deployment change (the MOKOSH_APPS_* / DRILLMARK_* compose-variables values per env) is tracked under DEV-362.

#BUNYIP-57

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fix(oidc): make post-logout URIs optional in SPA client upsert (BUNYIP-57)
All checks were successful
Check / fmt / clippy / build / test (pull_request) Successful in 1m38s
Create release / Create release from merged PR (pull_request) Has been skipped
c918f2e9b3
Gate the SPA OIDC client registration on only the two env vars login actually requires, *_REDIRECT_URIS and *_AUDIENCE, and treat *_POST_LOGOUT_REDIRECT_URIS as optional (the column is TEXT[] DEFAULT '{}'). Previously the upsert required all three vars and skipped the whole client when any was unset; a partial config (redirect + audience set, post-logout forgotten) would silently log "env vars unset" and leave the stale a8n.systems row in place, masking the very bug this change fixes. Now an absent post-logout var upserts an empty array instead of aborting registration.

#BUNYIP-57

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
nrupard deleted branch fix/bunyip-57-env-driven-oidc-client-seed 2026-06-08 19:24:27 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
psa-systems/bunyip!75
No description provided.