Commit graph

4 commits

Author SHA1 Message Date
Daniel
e2919e73c2 feat: no passwords here — sign-in belongs to the provider
Some checks failed
Tests / backend (push) Failing after 8s
Tests / frontend (push) Failing after 31s
Tests / e2e (push) Failing after 42s
The account settings offered "Change password", and there is no password
to change: accounts live at PedsHub SSO, which is also where a passkey,
an address or a second factor is set. A form that writes a credential
nothing checks is worse than no form — it invites somebody to secure
their account by a route that does not secure it. The panel now keeps
the display name and points at the provider for the rest.

Retired with it: GET /auth/verify-email, POST /auth/resend-verification,
POST /auth/forgot-password, POST /auth/reset-password, the new_password
branch of PUT /auth/me, the two schemas behind them, and the three pages
— VerifyEmailPage, ForgotPasswordPage, ResetPasswordPage — with their
routes and the links into them. An account with no password cannot
forget one.

POST /auth/login stays, still refused while sso_only is set. It is the
way back in if the provider is ever unreachable, together with the
DEFAULT_ADMIN_EMAIL seed at startup, and removing it would leave no door
at all on a bad day. The test that walked five password doors now walks
that one and asserts the other four answer 404 rather than 403 — gone,
not guarded.

Verified live: all four endpoints 404, and the account panel shows a
name field and "Manage your account ↗" to
sso.pedshub.com/if/user/#/settings, with no password field anywhere.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TqXevQJhxFrM7jJg82cgZN
2026-09-13 15:50:38 +02:00
Daniel
963ca04cf8 feat: no account creation here — the provider makes accounts
Some checks failed
Tests / backend (push) Failing after 7s
Tests / frontend (push) Successful in 30s
Tests / e2e (push) Failing after 33s
Gone: /auth/register, /auth/signup-policy, POST /admin/users, the
RegisterPage, the register half of the landing modal, the Register
button, the "Sign up" link, the "Create an account" hero button, and the
UserCreate schema. /register redirects to /login for anybody holding a
bookmark. A first admin on a fresh install still comes from
DEFAULT_ADMIN_EMAIL at startup, so nothing is locked out.

And no flash of the old way in. Both sign-in surfaces defaulted to "no
provider" and drew the email form while /auth/sso/config was in flight,
then swapped it — so a reload showed a form that does not exist, briefly,
every time. They render nothing until the answer arrives. The landing
modal is now one button, "Sign in with PedsHub SSO", with no sentence
under it: the button already says where you are going.

Also, the section strip takes the width it has. It sat inside the 1200px
measure that keeps an article readable, so on a wide desktop the last
entries fell off the end and a scroll arrow appeared beside acres of
empty space. Verified at 1280, 1600 and 1920: ten links, no arrows.

And "Make a deck" comes out of the strip and the phone menu — that was
an over-reach on my part. The landing CTA keeps it, pointing at
app.pedshub.com/#resources, which is what was actually asked for.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TqXevQJhxFrM7jJg82cgZN
2026-09-13 14:37:10 +02:00
Daniel
282b6a25f7 fix: no password sign-up offered on a site that signs in through a provider
Some checks failed
Tests / backend (push) Failing after 5s
Tests / frontend (push) Successful in 27s
Tests / e2e (push) Failing after 32s
/register drew the whole form under SSO-only — name, email, password
twice, invite code, Sign Up — and only the POST refused it. That is
asking somebody for four fields and a password they will never use
before telling them the door does not exist.

/auth/signup-policy is the question the form already asks, so it answers
it: sso_only, the provider's name, and registration_open false whenever
single sign-on is the only way in. The page says one line instead. The
Register tab on the landing modal and the Register button in the header
go with it — and the modal falls back to Sign in if it was opened
straight into Register.

The sign-in page needed nothing: it already hid its "Sign up" link on
registration_open false, which is now also true under SSO.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TqXevQJhxFrM7jJg82cgZN
2026-09-13 06:02:01 +02:00
Daniel
c9e0655d6e fix: shut every password door under SSO-only, and keep the token out of the URL
Some checks failed
Tests / backend (push) Failing after 9s
Tests / frontend (push) Successful in 29s
Tests / e2e (push) Failing after 27s
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