To install dependencies:
bun install
To run:
bun run dev
- forgot password -> reset password
- if no ip?
- what can/should be offloaded to db (ripples on delete)
- db/context typing
- rate limit headers
- enhance, move async email/password validation into schema
- error pages/views
- deal w reset flow explicitly
- preloading
- cacheing
- split tsconfigs for client/server
- error construction
- logging
- email util
- auth session cleanup
- otp input
- client/server auth handling
- form error handling
- styling
- password toggle
[x] GET /signup
- global GET rate limiter
- auth (lax)
- if auth, redirect to auth or home
- return signup page/form [x] POST /signup
- global POST rate limiter
- ip bucket checker
- validate payload, including strong email/password checks
- db connect
- check email availability
- verify password strength
- consume ip cost
- transaction
- create user
- create email verification request
- send verification email
- set email verification request cookie
- create auth token
- create auth session
- set auth session cookie
- redirect to /2fa/setup [x] GET /2fa/setup
- global GET rate limiter
- auth (strict)
- bounce to email verification
- bounce to 2fa
- generate totp key
- render qr svg
- return 2fa setup page/form [x] POST /2fa/setup
- global POST rate limiter
- auth (strict)
- bounce if not email verified
- bounce if 2fa setuped but not verified (covers reset)
- ip bucket check
- get code + encoded key from form
- decode key
- consume ip
- verify totp code
- mark sesion 2fa verified
- redirect to recovery code [x] GET /verify-email
- global GET rate limiter
- auth (strict)
- get token from cookie
- get request with token
- if no request, clear cookie
- if no request and user verified, bounce
- return page/form [x] POST /verify-email
- global POST rate limiter
- auth (strict)
- bounce if signed up but not in?
- ip bucket check
- get verification request from cookie
- bounce if no request or invalid
- validate payload
- consume ip bucket
- send new if expired, and throw
- throw if passed code is invalid
- delete existing requests
- delete password reset sessions (invalidate)
- update email + mark verified
- clear request cookie
- if not 2fa (duh redirect to setup)
- redirect home [x] POST /verify-email/resend
- global POST rate limiter
- auth (strict)
- bounce if signed up but not in?
- get request from cookie, throw if invalid
- throw if user already verified
- consume ip
- if no request, create new
- if request, create new
- send new email
- return ok [x] GET /recovery-code
- global GET rate limiter
- auth (strict)
- bounce through auth
- get recovery code
- return page [x] GET /
- global GET rate limiter
- auth (strict)
- bounce through auth
- return page w logout [x] POST /logout
- global POST rate limiter
- auth (strict)
- delete auth session
- invalidate cookie
- redirect login [x] GET /login
- global GET rate limiter
- auth (lax)
- bounce to auth if authed
- return page with form [x] POST /login
- global POST rate limiter
- ip bucket check
- validate payload
- get user or throw
- consupe ip bucket
- consume throttler
- get + verify hash
- if invalid, throw, else reset throttler
- gen auth token + session
- set session cookie
- if not email verified, redirect
- if not 2fa redirect [x] GET /2fa
- global GET rate limiter
- auth {strict}
- bounce to auth setup
- if 2fa verified, bounce to login
- return page with form [x] POST /2fa
- global POST rate limiter
- auth (strict)
- bounce if unauthed or verified
- shared ip check
- validate payload
- consume ip
- get user totp key or throw
- verify code or throw
- reset limiter
- mark 2fa verified
- redirect home [x] GET /2fa/reset
- global GET rate limiter
- auth (strict)
- bounce through auth
- bounce 2fa verified (meant for recovery)
- return verification form [x] POST /2fa/reset
- global POST rate limiter
- auth (strict)
- bounce if unauthed or verified
- user bucket checker
- validate payload
- consume user
- reset 2fa or throw on invalid
- reset bucket
- redirect to 2fa setup [x] GET /forgot-password
- global GET rate limiter
- return form [x] POST /forgot-password
- global POST rate limiter
- ip check
- validate payload
- find user by email or throw
- consume ip
- consume user
- delete existing reset sessions
- generate token
- create session
- send email
- set cookie
- redirect to email verification [x] GET /reset-password/verify-email
- global GET rate limiter
- get session from cookie
- if no session, redirect to /forgot-password
- if email verified, but no 2fa advance to 2fa
- if email verified && 2fa, advance to reset
- return page with code form [x] POST /reset-password/verify-email
- global POST rate limiter
- get reset session, throw on invalid
- bounce if email already verified
- ip bucket check
- validate payload
- consume ip
- reset bucket
- mark session verified
- mark user as verified if match, or throw
- redirect to 2fa [x] GET /reset-password/2fa
- global GET rate limiter
- get reset session, bounce on invalid
- if session not email verified, bounce
- if user not 2fa setup, advanced
- if session 2faed, advance
- return 2fa page [x] POST /reset-password/2fa/totp
- global POST rate limiter
- get password reset session or throw
- throw if auth not set up
- user bucket check
- validate payload
- get user key or throw
- consume user
- verify code or throw
- reset bucket
- mark session 2faed
- redirect to /reset-password [x] POST /reset-password/2fa/recovery
- global POST rate limiter
- get password reset session or throw
- throw if auth not set up
- user bucket check
- validate payload
- consume user
- reset with recovery code or throw
- reset bucket
- redirect to /reset-password [x] GET /reset-password
- global GET rate limiter
- get password reset session or bounce
- bounce if not 2fased
- return form [x] POST /reset-password
- global POST rate limiter
- get password reset session or throw
- throw if not verified, or 2faed
- validate payload
- delete reset sessions
- delete auth sessions
- update password
- generate auth session
- set cookie
- unset reset cookie
- redirect home [x] GET /settings
- global GET rate limiter
- auth (strict)
- bounce if not 2faed
- get user recovery code if 2fa setup
- return settings page [x] POST /settings/update-email
- global POST rate limiter
- auth (strict)
- bounce if not 2faed
- user bucket check
- validate payload
- check email availability
- consume user
- create new request
- send request
- set cookie
- redirect to /verify-email [x] POST /settings/update-password
- global POST rate limiter
- auth (strict)
- bounce if not 2faed
- check user bucket
- validate payload
- consume user bucket
- get + match password hash
- reset bucket
- delete auth sessions
- update password
- generate new auth session
- set cookie
- return ok [x] POST /settings/regenerate-recovery-code
- global POST rate limiter
- auth (strict)
- bounce if not verified or 2faed
- reset recovery code
- return ok