|
All checks were successful
PR build (required check) / changes (pull_request) Successful in 8s
secrets-guard / encrypted (pull_request) Successful in 5s
shell-lint / shellcheck (pull_request) Successful in 8s
PR build (required check) / validate-observability (pull_request) Has been skipped
PR build (required check) / build-frontend (pull_request) Successful in 1m11s
PR build (required check) / build-backend (pull_request) Successful in 1m35s
PR build (required check) / gate (pull_request) Successful in 1s
Discord was already linkable from a signed-in session; it could not authenticate one. Adds /discord/login/start, which resolves a Discord identity to an account and issues a session. Both flows return to the existing /discord/link/callback. Discord only honours redirect URIs registered in the developer portal, so a second callback path would have blocked this behind an operator change; the signed state now carries a purpose, and since that is inside the HMAC a state can only verify under the flow it was minted for. Account resolution, in order: an existing discord_id (the durable key, no email needed); otherwise the Discord email, but only when Discord reports it verified — that flag is the sole evidence the person owns the address, and matching on an unverified one would hand over the account. Failing both, a new account is created. A login state carries no user id, so it is replayable against whoever is signed in. It therefore never links: doing so would be a forced-linking takeover. Only link/start, whose state is bound to a user id, may link. Accounts created this way have no password their owner has ever seen, so user.discord_only records that and unlink is refused for them — no reset-password router is mounted, so unlinking would be unrecoverable. Migration 0003 adds the column conditionally: 0001 builds the schema from live model metadata, so a fresh database already has it. Also fixes two latent bugs in the link flow: the callback redirected to /subscriptions, which no route serves (it is /alerts), and nothing ever read the ?discord= status it has always sent, so a completed link gave no feedback. Both now surface as a toast. Linking a Discord account that another account already owns returned a 500 from the unique constraint; it now explains itself. |
||
|---|---|---|
| .. | ||
| 0001_initial_schema.py | ||
| 0002_climate_hypertables.py | ||
| 0003_user_discord_only.py | ||