fix(oidc): reconcile lets-chat client per-environment (LC-448) #217
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/LC-448-letschat-oidc-env-reconcile"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Tracking: LC-448 (subtask of LC-446 "Deploy to prod").
Problem
Production lets-chat-psa (nc-01,
chat.psa.systems, issuerapi.psa.systems) cannot complete login. The lets-chat confidential OIDC client (client_id b0000000-0000-4000-8000-00000000000c) is seeded by static migration20260618032217_register_lets_chat_oidc_client.sqlwithredirect_uris = ['https://chat.a8n.systems/auth/bunyip/callback'](the staging host). The same image runs on every bunyip-api, so the prod client carries the staging redirect and rejects the prod callback.This is exactly the class BUNYIP-57 fixed for the SPA clients (
mokosh-apps,drillmark) via the env-driven startup upsert (upsert_spa_oidc_client), whose comment notes the static migration "seeds env-blind staging (a8n.systems) URIs that break the PKCE flow on every other host." lets-chat was never added to that list.Change
Add
upsert_lets_chat_oidc_client(&pool)after the drillmark upsert. A confidential analogue ofupsert_spa_oidc_client:client_id;UPDATEs the row the migration pre-seeds (the migrator atmain.rsruns before this), writingredirect_uris/post_logout_redirect_uris/audiencefrom per-environmentLETS_CHAT_REDIRECT_URIS/LETS_CHAT_POST_LOGOUT_REDIRECT_URIS/LETS_CHAT_AUDIENCE.client_typeandtoken_endpoint_auth_methodare left at their migration values (confidential / client_secret_basic).LETS_CHAT_CLIENT_SECRET_HASH(Argon2id PHC string) pins a dedicated per-environment client secret viaCOALESCE(unset keeps the migration's shared hash), so production no longer shares a secret with staging.LETS_CHAT_REDIRECT_URIS+LETS_CHAT_AUDIENCEpresent (skip + log otherwise), mirroring the SPA path. Zero rows updated logs a warning rather than inserting a malformed client.lets-chat validates
id_token.aud == client_id(not theaudiencecolumn), soredirect_urisis what blocked the prod flow; audience is set for consistency but is not load-bearing for lets-chat.Operator action (new env vars per host)
Set in each bunyip-api compose (companion docker PR):
LETS_CHAT_REDIRECT_URIS=https://chat.psa.systems/auth/bunyip/callback,LETS_CHAT_POST_LOGOUT_REDIRECT_URIS=https://chat.psa.systems,LETS_CHAT_AUDIENCE=https://chat.psa.systems,LETS_CHAT_CLIENT_SECRET_HASH=<argon2id hash of the dedicated prod secret>.chat.a8n.systemsequivalents, no hash var (keeps the migration's shared hash).Verification
cargo fmt --all+cargo clippy --workspace --all-targets -- -D warningsgreen in the pinnedrust-builder-glibcimage. Change is bin-only (bunyip-api/src/main.rs);cargo test --workspace --libcompiles lib targets only and is unaffected. Runtime query (not thequery!macro), so no.sqlxcache entry needed.