Skip to content

feat(auth): harden the signup/login flow (verification, anti-abuse, OAuth, sessions)#172

Open
dantaspaulo wants to merge 1 commit into
trypostit:mainfrom
dantaspaulo:feat/auth-signup-hardening
Open

feat(auth): harden the signup/login flow (verification, anti-abuse, OAuth, sessions)#172
dantaspaulo wants to merge 1 commit into
trypostit:mainfrom
dantaspaulo:feat/auth-signup-hardening

Conversation

@dantaspaulo

Copy link
Copy Markdown
Contributor

Cohesive hardening of the public signup/login surface. No new dependencies, no new tables.

Security / robustness

  • Mandatory email verification with a magic link — a global EnsureEmailVerified middleware gates page navigation (non-JSON GET) until the address is confirmed. The signed verification link verifies and logs the user in, so it works even when opened on another device/browser. Social logins and invites are already verified; self-hosted skips the gate.
  • Fix your email before verifying (UpdateUnverifiedEmailController) — a user who mistyped their address at signup can change it and resend the link, only while unverified.
  • Close OAuth account-takeover — the Google/GitHub callback only links an existing account by email (or creates a verified one) when the provider confirmed that email: Google's OIDC email_verified claim, GitHub's /user/emails API. Otherwise it returns to login without linking.
  • Password reset/change drops active sessions — reset wipes all of the user's DB sessions; changing it in settings wipes the others. An attacker with an open session no longer survives the victim's reset.
  • Anti-enumeration on forgot-password — uniform response whether or not the email exists, plus a throttle.
  • Anti-abuse on registerthrottle:5,1, an autofill-proof honeypot (hidden contact_time, cleared on mount/autofill so it never blocks a real signup), disposable-email blocking (NotDisposableEmail, config-extensible), and a per-IP daily quota (0 disables).
  • Security headers + secure session cookieSecurityHeaders middleware (nosniff, X-Frame-Options, Referrer-Policy, Permissions-Policy, HSTS in prod) and session.secure defaulting to true in production.
  • Strict email validationEmail::defaults() (strict + native) so a@b, @localhost and whitespace are rejected everywhere a new email enters (register, reset, invite, email-fixup) before they bounce.

Signup UX

  • 45s resend cooldown with an animated countdown on the verify screen.
  • Real-time password-strength meter on register and reset, mirroring the backend rules.

Config

New config/trypost.php security.* (per-IP quota, disposable blocking, extra domains) and services.github.api (overridable GitHub API host).

Tests

RegistrationAbuseTest, OauthLinkingSecurityTest, AuthHardeningTest — 17 tests / 53 assertions covering the honeypot, disposable/quota/strict-email rules, the OAuth takeover fix, the verification gate, uniform forgot-password and session invalidation.

…Auth, sessions)

Cohesive hardening of the public auth surface, no new dependencies or tables:

- Mandatory email verification with a magic link: a global `EnsureEmailVerified`
  middleware gates page navigation until the address is confirmed; the signed
  verification link verifies AND logs the user in, so it works when opened on
  another device. Social logins/invites are already verified; self-hosted skips.
- Fix email-fixup: an unverified user who mistyped their email can correct it
  and resend the link (`UpdateUnverifiedEmailController`).
- 45s resend cooldown with a countdown on the verify screen.
- Anti-abuse on register: throttle, an autofill-proof honeypot, disposable-email
  blocking (`NotDisposableEmail`, config-extensible) and a per-IP daily quota.
- Close OAuth account-takeover: only link/create by email when the PROVIDER
  confirmed it (Google `email_verified` claim; GitHub `/user/emails`).
- Password reset/change now drops active DB sessions, so an attacker with an
  open session can't survive it.
- Uniform forgot-password response to stop email enumeration (+ throttle).
- `SecurityHeaders` middleware and a secure session cookie by default in prod.
- Strict email validation (`Email::defaults` strict + native) everywhere a new
  email enters, so a@b / @localhost / whitespace are rejected before they bounce.
- Real-time password-strength meter on register and reset.

Covered by RegistrationAbuse, AuthHardening and OauthLinkingSecurity tests.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant