Authentication Methods

Configure email/password, magic link, and OAuth authentication methods in your SaaS application.

Enable the authentication methods your users expect. The kit supports email/password, magic links (passwordless), and OAuth providers - toggle each with environment variables. This guidance applies to the Drizzle kit.

This page is part of the Authentication documentation.

Available Methods

MethodDefaultUse Case
Email/PasswordEnabledTraditional sign-in with credentials
Magic LinkDisabledPasswordless sign-in via email link
OAuthDisabledSocial login (Google, GitHub, etc.)

Most SaaS apps start with email/password. Add magic links for users who forget passwords frequently. Add OAuth to reduce sign-up friction.

Email and Password Authentication

Email/password is the most common auth method. Users register with their email and a password, then sign in with those credentials.

Enable/Disable

apps/web/.env.local

NEXT_PUBLIC_AUTH_PASSWORD=true

Set to false to disable email/password authentication entirely (useful if you only want OAuth).

Password Requirements

Configure password complexity rules:

apps/web/.env.local

NEXT_PUBLIC_PASSWORD_MIN_LENGTH=8
NEXT_PUBLIC_PASSWORD_MAX_LENGTH=99
NEXT_PUBLIC_PASSWORD_REQUIRE_SPECIAL_CHARS=true
NEXT_PUBLIC_PASSWORD_REQUIRE_NUMBERS=true
NEXT_PUBLIC_PASSWORD_REQUIRE_UPPERCASE=true
VariableDefaultDescription
NEXT_PUBLIC_PASSWORD_MIN_LENGTH8Minimum password length
NEXT_PUBLIC_PASSWORD_MAX_LENGTH99Maximum password length
NEXT_PUBLIC_PASSWORD_REQUIRE_SPECIAL_CHARStrueRequire special characters
NEXT_PUBLIC_PASSWORD_REQUIRE_NUMBERStrueRequire at least one number
NEXT_PUBLIC_PASSWORD_REQUIRE_UPPERCASEtrueRequire uppercase letter

Email Verification

Email verification is required by default. Users receive a verification email after registration and must click the link before accessing the app.

The verification flow:

  1. User registers with email/password
  2. Better Auth sends verification email
  3. User clicks verification link
  4. User is automatically signed in

Email verification protects against:

  • Fake accounts with invalid emails
  • Account takeover via typo-squatting
  • Spam registrations

Password Reset

Users can reset forgotten passwords:

  1. User clicks "Forgot password" on sign-in form
  2. User enters their email address
  3. Better Auth sends reset email with secure link
  4. User clicks link and sets new password
  5. User is signed in with new password

Reset links expire after a configurable time (default: 1 hour).

Magic links provide passwordless authentication. Users enter their email, receive a link, and click to sign in - no password needed.

apps/web/.env.local

NEXT_PUBLIC_AUTH_MAGIC_LINK=true

How It Works

  1. User enters email on sign-in form
  2. User clicks "Sign in with Magic Link"
  3. Better Auth sends email with secure link
  4. User clicks link in email
  5. User is signed in automatically

Implementation

The magic link plugin is configured in packages/better-auth/src/plugins/magic-link.ts:

import { magicLink } from 'better-auth/plugins/magic-link';
export const magicLinkPlugin = magicLink({
sendMagicLink: async ({ email, url }) => {
const { sendMagicLinkEmail } =
await import('../emails/send-magic-link-email');
// Log in development mode
if (process.env.NODE_ENV === 'development') {
console.log(`[DEV] Magic link for ${email}: ${url}`);
}
await sendMagicLinkEmail({
email,
url,
productName: getProductName(),
});
},
});

In development, magic links are logged to the console for easy testing without checking email.

Good for:

  • Users who frequently forget passwords
  • Low-friction onboarding
  • Apps where security > convenience (each sign-in requires email access)

Consider alternatives when:

  • Users sign in frequently (magic links add friction)
  • Email delivery is unreliable
  • Users need offline access

Client Usage

Sign In with Password

import { authClient } from '@kit/better-auth/client';
const result = await authClient.signIn.email({
email: 'user@example.com',
password: 'securepassword123',
});
if (result.error) {
console.error('Sign in failed:', result.error.message);
}
import { authClient } from '@kit/better-auth/client';
// Request magic link
await authClient.signIn.magicLink({
email: 'user@example.com',
callbackURL: '/dashboard',
});
// User receives email and clicks link to complete sign-in

Sign Up

import { authClient } from '@kit/better-auth/client';
const result = await authClient.signUp.email({
email: 'user@example.com',
password: 'securepassword123',
name: 'John Doe',
});
if (result.error) {
console.error('Sign up failed:', result.error.message);
}

Password Reset

import { authClient } from '@kit/better-auth/client';
// Request password reset
await authClient.forgetPassword({
email: 'user@example.com',
redirectTo: '/auth/password-reset',
});
// After user clicks email link and enters new password
await authClient.resetPassword({
newPassword: 'newSecurePassword123',
});

Common Pitfalls

  • Disabling all auth methods: At least one method must be enabled.
  • Weak password requirements in production: Use strong defaults. Weak passwords are a security risk.
  • Not testing email delivery: Magic links and verification emails require working email configuration.
  • Missing NEXT_PUBLIC_ prefix: Auth method toggles need the public prefix to work on the client.

Frequently Asked Questions

Can I enable both password and magic link authentication?
Yes. Users can choose their preferred method on the sign-in form. The UI adapts to show available options based on environment variables.
How do I customize password requirements?
Set the NEXT_PUBLIC_PASSWORD_* environment variables. Changes apply to new registrations and password changes.
What happens if email verification fails?
Users can request a new verification email. The original link expires but the account remains valid.
Can I disable email verification?
Yes, but this is not recommended for production. Set requireEmailVerification to false in auth.ts. This allows unverified accounts to access the app.
How long do magic links last?
Magic links expire after a configurable time (default varies by Better Auth version). Users can request a new link if theirs expires.

Next: Social Providers →