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 { signUpAction } from '~/lib/auth/actions';

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

Server Action Implementation

'use server';

import { enhanceAction } from '@kit/next/actions';
import { z } from 'zod';

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

export const signUpAction = enhanceAction(
  async (data) => {
    const client = getSupabaseServerClient();

    const { data: authData, error } = await client.auth.signUp({
      email: data.email,
      password: data.password,
      options: {
        emailRedirectTo: `${process.env.NEXT_PUBLIC_SITE_URL}/auth/callback`,
      },
    });

    if (error) throw error;

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

Sign Up Component

'use client';

import { useForm } from 'react-hook-form';
import { signUpAction } from '../_lib/actions';

export function SignUpForm() {
  const { register, handleSubmit, formState: { errors } } = useForm();

  const onSubmit = async (data) => {
    const result = await signUpAction(data);

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

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <div>
        <label>Email</label>
        <input
          type="email"
          {...register('email', { required: true })}
        />
        {errors.email && <span>Email is required</span>}
      </div>

      <div>
        <label>Password</label>
        <input
          type="password"
          {...register('password', { required: true, minLength: 8 })}
        />
        {errors.password && <span>Password must be 8+ characters</span>}
      </div>

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

Sign In Flow

User Login

export const signInAction = enhanceAction(
  async (data) => {
    const client = getSupabaseServerClient();

    const { error } = await client.auth.signInWithPassword({
      email: data.email,
      password: data.password,
    });

    if (error) throw error;

    redirect('/home');
  },
  { schema: SignInSchema }
);

Sign In Component

'use client';

export function SignInForm() {
  const { register, handleSubmit } = useForm();

  const onSubmit = async (data) => {
    try {
      await signInAction(data);
    } catch (error) {
      toast.error('Invalid email or password');
    }
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        type="email"
        {...register('email')}
        placeholder="Email"
      />
      <input
        type="password"
        {...register('password')}
        placeholder="Password"
      />
      <button type="submit">Sign In</button>
    </form>
  );
}

Email Verification

Requiring Email Confirmation

Configure in Supabase dashboard or config:

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

Handling Unconfirmed Emails

export const signInAction = enhanceAction(
  async (data) => {
    const client = getSupabaseServerClient();

    const { data: authData, error } = await client.auth.signInWithPassword({
      email: data.email,
      password: data.password,
    });

    if (error) {
      if (error.message.includes('Email not confirmed')) {
        return {
          success: false,
          error: 'Please confirm your email before signing in',
        };
      }
      throw error;
    }

    redirect('/home');
  },
  { schema: SignInSchema }
);

Password Reset

Request Password Reset

export const requestPasswordResetAction = enhanceAction(
  async (data) => {
    const client = getSupabaseServerClient();

    const { error } = await client.auth.resetPasswordForEmail(data.email, {
      redirectTo: `${process.env.NEXT_PUBLIC_SITE_URL}/auth/reset-password`,
    });

    if (error) throw error;

    return {
      success: true,
      message: 'Check your email for reset instructions',
    };
  },
  {
    schema: z.object({
      email: z.string().email(),
    }),
  }
);

Reset Password Form

'use client';

export function PasswordResetRequestForm() {
  const { register, handleSubmit } = useForm();

  const onSubmit = async (data) => {
    const result = await requestPasswordResetAction(data);

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

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        type="email"
        {...register('email')}
        placeholder="Enter your email"
      />
      <button type="submit">Send Reset Link</button>
    </form>
  );
}

Update Password

export const updatePasswordAction = enhanceAction(
  async (data) => {
    const client = getSupabaseServerClient();

    const { error } = await client.auth.updateUser({
      password: data.newPassword,
    });

    if (error) throw error;

    redirect('/home');
  },
  {
    schema: z.object({
      newPassword: z.string().min(8),
    }),
  }
);

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 { getSupabaseServerClient } from '@kit/supabase/server-client';

export async function requireAuth() {
  const client = getSupabaseServerClient();
  const { data: { user } } = await client.auth.getUser();

  if (!user) {
    redirect('/auth/sign-in');
  }

  return user;
}

Sign Out

export const signOutAction = enhanceAction(
  async () => {
    const client = getSupabaseServerClient();
    await client.auth.signOut();
    redirect('/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