Raypx

Email & Password

Setting up email/password authentication with verification emails.

Email/password authentication is enabled by default in Raypx. Users can sign up with an email address and password, receive a verification email, and later reset their password if needed.

Environment Variables

Add these to your .env file:

.env
# Required -- minimum 32 characters
AUTH_SECRET=your-secret-key-at-least-32-characters-long

# Base URL for auth callbacks
AUTH_URL=http://localhost:3000

# Email sending (Resend)
AUTH_RESEND_KEY=re_your_resend_api_key
RESEND_FROM=noreply@yourdomain.com
VariableRequiredDescription
AUTH_SECRETYesMinimum 32 characters. Used to sign session tokens.
AUTH_URLYesThe public URL of your app. Used for email links.
AUTH_RESEND_KEYFor emailResend API key (starts with re_).
RESEND_FROMFor emailSender email address, must be verified in Resend.
AUTH_EMAIL_FROM_NAMENoDisplay name for emails. Defaults to Raypx.

Without AUTH_RESEND_KEY, the email transport falls back to a logger transport that prints emails to the console. This is useful for local development.

Server Configuration

The auth server is configured in packages/auth/src/index.ts:

packages/auth/src/index.ts
return betterAuth({
  database: drizzleAdapter(db, { provider: "pg", schema }),
  emailAndPassword: {
    enabled: true,
    resetPasswordTokenExpiresIn: 60 * 60, // 1 hour
    revokeSessionsOnPasswordReset: true,
    sendResetPassword: async ({ user, url }) => {
      await sendPasswordResetEmail({ email: user.email, name: user.name, url })
    },
    onPasswordReset: async ({ user }) => {
      logger.info("auth.password.reset", { feature: "auth", userId: user.id })
    },
  },
  // ...
})

Key settings:

  • Password reset token expires in 1 hour.
  • All sessions are revoked when a password is reset for security.
  • The sendResetPassword callback sends an email using the @raypx/email package.

Client Usage

Sign Up

import { authClient } from "@/lib/auth-client"

await authClient.signUp.email({
  name: "Jane Doe",
  email: "jane@example.com",
  password: "securepassword123",
}, {
  onSuccess: () => {
    toast.success("Account created! Please check your email to verify.")
  },
  onError: (error) => {
    toast.error(error.error.message)
  },
})

Sign In

import { authClient } from "@/lib/auth-client"

await authClient.signIn.email({
  email: "jane@example.com",
  password: "securepassword123",
}, {
  onSuccess: () => {
    navigate({ to: "/dashboard" })
  },
  onError: (error) => {
    toast.error(error.error.message)
  },
})

Sign Out

authClient.signOut({
  fetchOptions: {
    onSuccess: () => navigate({ to: "/" }),
  },
})

Session Hook

const { data: session, isPending } = authClient.useSession()

if (isPending) return <Loader />
if (!session) return <Navigate to="/login" />

return <span>Welcome, {session.user.name}</span>

Password Requirements

The sign-in form validates passwords client-side with a minimum length of 8 characters. Better Auth handles server-side hashing automatically using bcrypt.

Email Templates

Email templates are defined in the @raypx/email package using React Email components:

Each template renders both HTML and plain-text versions. You can customize the styling and content by editing these files. See Email Verification & Password Reset for more details.

On this page