-
Couldn't load subscription status.
- Fork 643
Description
Users complain that magic links don't work reliably. So I want to remove magic link support and change this to use passwords.
To do this, we'll need to have a process for users who don't have a password yet (effectively have them go through the reset password flow when they need to login, make sure it's really clear that things have changed). We'll need to add a password (patterned after the Epic Stack approach to passwords). Below is a description of how auth works in the Epic Stack. Use this as a guide of how to implement support for password auth.
If you would like, check https://github.com/epicweb-dev/epic-stack for details. Feel free to git clone https://github.com/epicweb-dev/epic-stack.git in a temporary directory to reference the code itself.
Epic Stack Password Authentication Architecture
1. User Signup Flow
- Step 1: User enters their email on the signup page.
- Step 2: The backend checks if the email is already registered.
- Step 3: If not, a TOTP code is generated and emailed to the user, along with a verification link.
- Step 4: User enters the code or clicks the link, which leads to the verification route.
- Step 5: On successful code verification, the email is stored in a session and the user is redirected to the onboarding page.
- Step 6: Onboarding collects username, name, password, and agreement to terms.
- Step 7: The backend checks for username uniqueness and password strength (see below).
- Step 8: The password is hashed using bcrypt and stored in the database.
- Step 9: A session is created for the user.
2. Password Hashing and Storage
- Passwords are hashed using bcrypt with a cost factor of 10.
- The hash is stored in a related
Passwordmodel in the database. - Passwords are never stored or transmitted in plain text.
3. Login Flow
- Step 1: User enters username and password.
- Step 2: The backend fetches the user and their password hash.
- Step 3: The password is compared using bcrypt.
- Step 4: If valid, a session is created and stored in the database.
- Step 5: The session ID is stored in a cookie using session storage.
4. Session Management
- Sessions are stored in the database with an expiration date (default: 30 days).
- Session IDs are stored in cookies.
- Session validation checks for expiration and existence.
- Logging out destroys the session both in the database and in the browser.
5. Password Reset Flow
- Step 1: User requests a password reset by entering their username or email.
- Step 2: Backend verifies the user exists.
- Step 3: A TOTP code is generated and emailed to the user.
- Step 4: User enters the code on the verification page.
- Step 5: On successful verification, the username is stored in a session and the user is redirected to the reset password page.
- Step 6: User enters a new password (checked for strength and commonality).
- Step 7: Password is hashed and updated in the database.
6. Password Strength and Commonality Checks
- Passwords are checked against the Pwned Passwords API to prevent use of common or compromised passwords.
- If the password is found in the API response, the user is prompted to choose a stronger password.
7. TOTP Verification for Email and Password Reset
- TOTP codes are generated for email verification and password reset flows.
- Codes are stored in a
verificationmodel in the database with expiration. - Verification routes validate the code before allowing onboarding or password reset.
8. Error Handling and Security
- All flows avoid leaking whether an email or username is registered (prevents user enumeration).
- Errors are handled gracefully and returned to the frontend for display.
- Sessions are destroyed on logout or after password reset.
Key Implementation Details
- Password Hashing:
Uses bcrypt (getPasswordHashandverifyUserPassword). - Session Storage:
Uses a custom session storage (authSessionStorage) and stores session IDs in cookies. - Verification Codes:
Uses TOTP for email and password reset verification, stored in the database. - Password Reset:
Requires code verification before allowing password change. - Common Password Check:
Uses SHA-1 hash and Pwned Passwords API to check password strength.
Suggested File Structure
auth.server.ts: Core authentication logic (signup, login, password hashing, session management).signup.tsx,onboarding.tsx: Signup and onboarding flows.login.tsx: Login flow.forgot-password.tsx,reset-password.tsx: Password reset flows.verify.tsx,verify.server.ts: Verification code handling.reset-password.server.ts,onboarding.server.ts: Server-side logic for onboarding and password reset verification.
Summary
This architecture provides a secure, user-friendly password authentication system with email verification, password strength checks, and robust session management. It avoids common pitfalls like user enumeration and weak password acceptance, and is extensible for