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, });
Building a Production-Grade Passwordless Authentication System
7 / 12