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

Event Hire Across Melbourne

One Click Events provides professional event hire services across all Melbourne suburbs including photo booths, DJ services, LED dance floors, games, and decorations. Free delivery within 50km of Melbourne CBD. Serving Inner City, Eastern, South Eastern, Northern, and Western suburbs.