Email & Password

Traditional email and password authentication.

Note: This is mock/placeholder content for demonstration purposes.

Email and password authentication is the traditional way users sign up and sign in.

Overview

Email/password authentication provides:

  • User registration with email verification
  • Secure password storage
  • Password reset functionality
  • Session management

Sign Up Flow

User Registration

import { signUpFunction } from '~/lib/auth/functions';

const result = await signUpFunction({
  email: 'user@example.com',
  password: 'SecurePassword123!',
});

Server Function Implementation

import { createServerFn } from '@tanstack/react-start';
import * as z from 'zod';

import { errorMiddleware } from '@kit/function-middleware/server';

const SignUpSchema = z.object({
  email: z.email(),
  password: z.string().min(8),
});

export const signUpFunction = createServerFn({ method: 'POST' })
  .middleware([errorMiddleware])
  .validator(SignUpSchema)
  .handler(async ({ data }) => {
    // Sign up user with auth service
    const authData = await signUp({
      email: data.email,
      password: data.password,
      options: {
        emailRedirectTo: `${process.env.VITE_SITE_URL}/auth/callback`,
      },
    });

    return { success: true, data: authData };
  });

Sign Up Component

'use client';

import { useForm } from '@tanstack/react-form';
import { useMutation } from '@tanstack/react-query';

import { Field, FieldError, FieldLabel } from '@kit/ui/field';
import { Input } from '@kit/ui/input';
import { toast } from '@kit/ui/sonner';

import { signUpFunction } from '../_lib/functions';

export function SignUpForm() {
  const signUp = useMutation({ mutationFn: signUpFunction });

  const form = useForm({
    defaultValues: { email: '', password: '' },
    validators: { onSubmit: SignUpSchema },
    onSubmit: async ({ value }) => {
      const result = await signUp.mutateAsync({ data: value });

      if (result.success) {
        toast.success('Check your email to confirm your account');
      }
    },
  });

  return (
    <form
      onSubmit={(e) => {
        e.preventDefault();
        void form.handleSubmit();
      }}
    >
      <form.Field name="email">
        {(field) => (
          <Field>
            <FieldLabel>Email</FieldLabel>
            <Input
              type="email"
              value={field.state.value}
              onBlur={field.handleBlur}
              onChange={(e) => field.handleChange(e.target.value)}
            />
            <FieldError errors={field.state.meta.errors} />
          </Field>
        )}
      </form.Field>

      <form.Field name="password">
        {(field) => (
          <Field>
            <FieldLabel>Password</FieldLabel>
            <Input
              type="password"
              value={field.state.value}
              onBlur={field.handleBlur}
              onChange={(e) => field.handleChange(e.target.value)}
            />
            <FieldError errors={field.state.meta.errors} />
          </Field>
        )}
      </form.Field>

      <button type="submit">Sign Up</button>
    </form>
  );
}

Sign In Flow

User Login

export const signInFunction = createServerFn({ method: 'POST' })
  .middleware([errorMiddleware])
  .validator(SignInSchema)
  .handler(async ({ data }) => {
    await signIn({
      email: data.email,
      password: data.password,
    });

    throw redirect({ href: '/home' });
  });

Sign In Component

'use client';

import { useForm } from '@tanstack/react-form';

import { Field } from '@kit/ui/field';
import { Input } from '@kit/ui/input';
import { toast } from '@kit/ui/sonner';

export function SignInForm() {
  const form = useForm({
    defaultValues: { email: '', password: '' },
    onSubmit: async ({ value }) => {
      try {
        await signInFunction({ data: value });
      } catch (error) {
        toast.error('Invalid email or password');
      }
    },
  });

  return (
    <form
      onSubmit={(e) => {
        e.preventDefault();
        void form.handleSubmit();
      }}
    >
      <form.Field name="email">
        {(field) => (
          <Field>
            <Input
              type="email"
              placeholder="Email"
              value={field.state.value}
              onBlur={field.handleBlur}
              onChange={(e) => field.handleChange(e.target.value)}
            />
          </Field>
        )}
      </form.Field>

      <form.Field name="password">
        {(field) => (
          <Field>
            <Input
              type="password"
              placeholder="Password"
              value={field.state.value}
              onBlur={field.handleBlur}
              onChange={(e) => field.handleChange(e.target.value)}
            />
          </Field>
        )}
      </form.Field>

      <button type="submit">Sign In</button>
    </form>
  );
}

Email Verification

Requiring Email Confirmation

Configure in auth config:

// config/auth.config.ts
export const authConfig = {
  requireEmailConfirmation: true,
};

Handling Unconfirmed Emails

export const signInFunction = createServerFn({ method: 'POST' })
  .middleware([errorMiddleware])
  .validator(SignInSchema)
  .handler(async ({ data }) => {
    try {
      await signIn({
        email: data.email,
        password: data.password,
      });
    } catch (error) {
      if (error.message.includes('Email not confirmed')) {
        return {
          success: false,
          error: 'Please confirm your email before signing in',
        };
      }
      throw error;
    }

    throw redirect({ href: '/home' });
  });

Password Reset

Request Password Reset

export const requestPasswordResetFunction = createServerFn({ method: 'POST' })
  .middleware([errorMiddleware])
  .validator(z.object({ email: z.email() }))
  .handler(async ({ data }) => {
    await requestPasswordReset({
      email: data.email,
      redirectTo: `${process.env.VITE_SITE_URL}/auth/reset-password`,
    });

    return {
      success: true,
      message: 'Check your email for reset instructions',
    };
  });

Reset Password Form

'use client';

import { useForm } from '@tanstack/react-form';

import { Field } from '@kit/ui/field';
import { Input } from '@kit/ui/input';
import { toast } from '@kit/ui/sonner';

export function PasswordResetRequestForm() {
  const form = useForm({
    defaultValues: { email: '' },
    onSubmit: async ({ value }) => {
      const result = await requestPasswordResetFunction({ data: value });

      if (result.success) {
        toast.success(result.message);
      }
    },
  });

  return (
    <form
      onSubmit={(e) => {
        e.preventDefault();
        void form.handleSubmit();
      }}
    >
      <form.Field name="email">
        {(field) => (
          <Field>
            <Input
              type="email"
              placeholder="Enter your email"
              value={field.state.value}
              onBlur={field.handleBlur}
              onChange={(e) => field.handleChange(e.target.value)}
            />
          </Field>
        )}
      </form.Field>

      <button type="submit">Send Reset Link</button>
    </form>
  );
}

Update Password

export const updatePasswordFunction = createServerFn({ method: 'POST' })
  .middleware([errorMiddleware])
  .validator(z.object({ newPassword: z.string().min(8) }))
  .handler(async ({ data }) => {
    await updatePassword({
      password: data.newPassword,
    });

    throw redirect({ href: '/home' });
  });

Password Requirements

Validation Schema

const PasswordSchema = z
  .string()
  .min(8, 'Password must be at least 8 characters')
  .regex(/[A-Z]/, 'Password must contain an uppercase letter')
  .regex(/[a-z]/, 'Password must contain a lowercase letter')
  .regex(/[0-9]/, 'Password must contain a number')
  .regex(/[^A-Za-z0-9]/, 'Password must contain a special character');

Password Strength Indicator

'use client';

import { useState } from 'react';

export function PasswordInput() {
  const [password, setPassword] = useState('');
  const strength = calculatePasswordStrength(password);

  return (
    <div>
      <input
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
      />
      <div className="flex gap-1">
        {[1, 2, 3, 4].map((level) => (
          <div
            key={level}
            className={cn(
              'h-1 flex-1 rounded',
              strength >= level ? 'bg-green-500' : 'bg-gray-200'
            )}
          />
        ))}
      </div>
      <span className="text-sm">
        {strength === 4 && 'Strong password'}
        {strength === 3 && 'Good password'}
        {strength === 2 && 'Fair password'}
        {strength === 1 && 'Weak password'}
      </span>
    </div>
  );
}

Session Management

Checking Authentication Status

import { getSession } from '@kit/auth/server';

export async function requireAuth() {
  const session = await getSession();

  if (!session) {
    throw redirect({ href: '/auth/sign-in' });
  }

  return session.user;
}

Sign Out

export const signOutFunction = createServerFn({ method: 'POST' })
  .middleware([errorMiddleware])
  .handler(async () => {
    await signOut();
    throw redirect({ href: '/auth/sign-in' });
  });

Security Best Practices

  1. Enforce strong passwords - Minimum 8 characters, mixed case, numbers, symbols
  2. Rate limit login attempts - Prevent brute force attacks
  3. Use HTTPS only - Encrypt data in transit
  4. Enable email verification - Confirm email ownership
  5. Implement account lockout - After failed attempts
  6. Log authentication events - Track sign-ins and failures
  7. Support 2FA - Add extra security layer