Skip to content

Add support for password auth #561

@kentcdodds

Description

@kentcdodds

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 Password model 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 verification model 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 (getPasswordHash and verifyUserPassword).
  • 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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions