pdf-quiz-generator/backend/app
Daniel c9e0655d6e
Some checks failed
Tests / backend (push) Failing after 9s
Tests / frontend (push) Successful in 29s
Tests / e2e (push) Failing after 27s
fix: shut every password door under SSO-only, and keep the token out of the URL
Two findings from a security pass on the SSO path, both real.

sso_only gated login and the login codes and nothing else. Register,
forgot-password, reset-password, resend-verification and setting a
password through PUT /auth/me all went through — so a site running
single sign-on could still mint a password account nobody vetted, and if
the flag were ever turned off, there it would be. One helper, five doors,
403 with a reason at each.

And the access token travelled in a query string. The SSO redirect is a
page load, so the browser asked nginx for /sso-callback?token=<a live
bearer token, good for a day> and nginx logs the request line — every
sign-in wrote one into the frontend container's access log, the
browser's history, and the Referer of whatever loaded next. It carries a
one-time code now: a random 32 bytes parked in Redis for sixty seconds,
traded at POST /auth/sso/exchange for the token, and deleted as it is
read, so a code replayed from any of those places buys nothing.

Also the OIDC state cookie, which is what stops an authorization
response being replayed at you: same_site lax (strict drops it on the
provider's top-level GET and fails every sign-in) and secure whenever
APP_URL is https.

And one cross, not two. The header's menu button is already a cross
while a drawer is open, so the drawer's own close button was a second
control an inch below it for the same job — gone from the article, the
player and the review.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TqXevQJhxFrM7jJg82cgZN
2026-09-13 05:55:37 +02:00
..
api feat: a versioned API, refresh tokens, and an end-to-end stack that found four bugs 2026-09-13 01:23:38 +02:00
middleware
models feat: a versioned API, refresh tokens, and an end-to-end stack that found four bugs 2026-09-13 01:23:38 +02:00
routers fix: shut every password door under SSO-only, and keep the token out of the URL 2026-09-13 05:55:37 +02:00
schemas fix: shut every password door under SSO-only, and keep the token out of the URL 2026-09-13 05:55:37 +02:00
services feat: SSO reads roles from the provider's groups, and needs a verified address 2026-09-13 05:20:52 +02:00
tasks fix: Reading is published, Editorial has its own address, and repeat works 2026-09-13 04:16:46 +02:00
utils feat: a versioned API, refresh tokens, and an end-to-end stack that found four bugs 2026-09-13 01:23:38 +02:00
__init__.py
cli.py refactor: remove the LMS 2026-09-12 23:27:51 +02:00
config.py feat: SSO reads roles from the provider's groups, and needs a verified address 2026-09-13 05:20:52 +02:00
database.py
logging_config.py
main.py fix: shut every password door under SSO-only, and keep the token out of the URL 2026-09-13 05:55:37 +02:00