Print View

Building a Production-Grade Passwordless Authentication System

Ben Houston · 2026-03-18 · 12 slides

Use the browser print dialog to print or save this deck as a PDF.

  1. Slide 1

    Building a Production-Grade Passwordless Authentication System

    Passkeys, OTP codes, and account-enumeration-safe login flows

    Ben Houston · 2026-03-18

    How to combine passkeys and email OTP codes into a fast, resilient, phishing-resistant authentication system.

  2. Slide 2

    The Tech Stack

    App platform

    • TanStack Start
    • React
    • Server Functions

    Routing and data

    • TanStack Router
    • Type-safe file routing
    • Tight client/server flow control

    Auth and storage

    • Drizzle ORM
    • SQLite
    • JWT for verification links
    • WebAuthn for passkeys
  3. Slide 3

    Why Traditional Login Flows Fall Short

    Passwords are a security nightmare

    • Users reuse them across sites.
    • One breach can unlock multiple accounts.
    • Complexity rules push people to forget or write them down.
    • The real fix is to remove the password entirely.

    Authenticator apps are still a hack

    • They require a second device.
    • Copying 6-digit codes adds friction.
    • They can still be phished in real time.
    • Losing the device can lock users out.
  4. Slide 4

    The Ideal User Flow

    Primary auth

    1. Start passkey discovery mode
    2. Let the device discover the account
    3. Confirm with Face ID, Touch ID, or PIN

    Fallback auth

    1. Request an OTP code by email
    2. Enter the code in the same browser session
    3. Complete login without a password

    Why this works

    • No secret to remember
    • Phishing resistant
    • Works even when passkeys are unavailable
    • Avoids account enumeration
  5. Slide 5

    Logging In With Email OTP Codes

    Simple but secure

    • Send a unique, time-limited 8-character alphanumeric code.
    • Access to the email account becomes proof of identity.
    • The code must be entered in the same browser session.
    • Users can read the email on one device and sign in on another.

    Flow

    1. User requests a login code
    2. Server creates an auth attempt
    3. Email delivers the code
    4. User enters the code
    5. Server verifies the attempt and signs the user in
  6. Slide 6

    Why 8-Character Alphanumeric Codes?

    Security requirement

    Authenticator app codes are usually a second factor. In this system, the OTP code is the only factor, so it needs dramatically more entropy.

    The math

    • 6-digit numeric: 10^6 = 1,000,000 combinations
    • 8-character alphanumeric: 36^8 = 2,821,109,907,456 combinations

    Why it matters

    1. Brute-force attacks become computationally infeasible.
    2. Rate limiting still matters, but the large keyspace adds defense in depth.
    3. The OTP is strong enough to serve as primary authentication.
  7. Slide 7

    OTP Implementation Details

    Storage model

    • Generate an 8-character code from A-Z and 0-9.
    • Hash the code with SHA-256.
    • Store the hash in userAuthAttempts.
    • Put only the auth attempt reference in the JWT.
    • Mark attempts as expiring and one-time use.
    const codeHash = hashOTPCode(code); await db.insert(userAuthAttempts).values({ email: user.email, userId: user.id, codeHash, purpose: 'login', expiresAt: new Date(Date.now() + 15 * 60 * 1000), used: false, });

    Token verification

    • Verify the token shape before rendering the page.
    • Keep actual code verification on the server.
    • Avoid flashing authorized UI to unauthorized users.
    export const Route = createFileRoute('/login-via-code/$token')({ beforeLoad: async ({ params }) => { await verifyCodeVerificationToken(params.token); return { tokenValid: true }; }, component: LoginViaCodePage, });
  8. Slide 8

    Signup and Recovery Reuse the Same Foundation

    Signup flow

    1. User enters name and email.
    2. Server generates an OTP code.
    3. Store the code hash with purpose signup.
    4. Send the email.
    5. Only create the user after successful verification.

    Why this matters

    • Email ownership is proven before account creation.
    • The flow stays simple and consistent.
    • OTP codes also become the recovery path if a user loses a passkey.
  9. Slide 9

    Why Passkeys?

    Security benefits

    • Built on FIDO2 and WebAuthn
    • Browser-enforced origin binding
    • Strong phishing resistance
    • Private key never leaves the device

    User experience

    1. Device generates a key pair during registration.
    2. Server stores only the public key.
    3. Server sends a challenge during login.
    4. Device signs it with the private key.
    5. User confirms with biometrics or local device auth.
  10. Slide 10

    WebAuthn Implementation

    Stored credential data

    • Persist the public key
    • Persist the signature counter
    • Optionally persist transports
    • Never store the private key
    export const passkeys = sqliteTable('passkeys', { publicKey: text('public_key').notNull(), counter: integer('counter').notNull().default(0), transports: text('transports'), });

    Registration rules

    • Use @simplewebauthn/server
    • Require residentKey: 'required'
    • Require userVerification: 'required'
    • Prefer platform authenticators for one-click login
    authenticatorSelection: { residentKey: 'required', userVerification: 'required', authenticatorAttachment: 'platform', }
  11. Slide 11

    Security Considerations

    Rate limiting

    • Limit send-email requests by IP
    • Limit requests by target email
    • Add backoff for repeated failures
    • Prevent email bombing and token abuse

    Account enumeration

    • Passkeys in discovery mode avoid email input entirely
    • Email login always returns success
    • Existing accounts get a code email
    • Missing accounts get a notification email instead

    Signup safety

    • Verify email ownership first
    • Create the User record last
    • Keep auth attempts purpose-specific
    • Treat recovery and signup as first-class flows
  12. Slide 12

    The End State

    • Passkeys deliver the fastest and most phishing-resistant experience.
    • Email OTP provides a reliable fallback and recovery path.
    • Verification-first flows prevent premature account creation.
    • Enumeration-safe responses protect user privacy.
    • Rate limiting turns a clever auth design into a production-safe one.