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:
# 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| Variable | Required | Description |
|---|---|---|
AUTH_SECRET | Yes | Minimum 32 characters. Used to sign session tokens. |
AUTH_URL | Yes | The public URL of your app. Used for email links. |
AUTH_RESEND_KEY | For email | Resend API key (starts with re_). |
RESEND_FROM | For email | Sender email address, must be verified in Resend. |
AUTH_EMAIL_FROM_NAME | No | Display 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:
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
sendResetPasswordcallback sends an email using the@raypx/emailpackage.
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.