Bỏ qua

Email/Password Authentication

Email Auth Flow

Signup → Confirm → Login

┌─────────────────────────────────────────────────────────────┐
│                    EMAIL AUTH FLOW                           │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  1. SIGNUP                                                   │
│     User ──▶ signUp(email, password)                        │
│          ◀── Confirmation email sent                        │
│                                                              │
│  2. CONFIRM EMAIL                                            │
│     User ──▶ Clicks link in email                           │
│          ◀── Email confirmed, can login                     │
│                                                              │
│  3. LOGIN                                                    │
│     User ──▶ signInWithPassword(email, password)            │
│          ◀── JWT tokens (access + refresh)                  │
│                                                              │
│  4. SESSION                                                  │
│     Client ──▶ Stores tokens                                │
│           ──▶ Auto-refresh khi hết hạn                      │
│                                                              │
└─────────────────────────────────────────────────────────────┘

Sign Up

Basic Signup

const { data, error } = await supabase.auth.signUp({
  email: 'user@example.com',
  password: 'secure-password-123',
});

if (error) {
  console.error('Signup error:', error.message);
  return;
}

// data.user exists but email not confirmed yet
// data.session is null until confirmed (nếu email confirmation enabled)
console.log('User created:', data.user?.id);

Signup với User Metadata

const { data, error } = await supabase.auth.signUp({
  email: 'user@example.com',
  password: 'secure-password-123',
  options: {
    data: {
      full_name: 'John Doe',
      avatar_url: 'https://example.com/avatar.jpg',
      role: 'member',
    },
    // Custom redirect sau khi confirm email
    emailRedirectTo: 'https://myapp.com/welcome',
  },
});

// Metadata được lưu trong auth.users.raw_user_meta_data

Email Confirmation

Enable/Disable trong Dashboard

Supabase Dashboard
└── Authentication
    └── Providers
        └── Email
            ├── Enable Email Signup: ON
            ├── Confirm Email: ON/OFF
            └── Double Confirm Email Changes: ON

Custom Email Templates

Dashboard → Authentication → Email Templates

Templates available:
- Confirm signup
- Invite user
- Magic link
- Change email address
- Reset password

Email Template Variables

<!-- Available variables -->
{{ .ConfirmationURL }}  - Link xác nhận
{{ .Email }}            - Email người dùng
{{ .Token }}            - Token (nếu cần)
{{ .TokenHash }}        - Hashed token
{{ .SiteURL }}          - Your app URL

Sign In

Basic Sign In

const { data, error } = await supabase.auth.signInWithPassword({
  email: 'user@example.com',
  password: 'secure-password-123',
});

if (error) {
  // Common errors:
  // - Invalid login credentials
  // - Email not confirmed
  console.error('Login error:', error.message);
  return;
}

// Success: data.session contains tokens
console.log('Logged in:', data.user?.email);
console.log('Access token:', data.session?.access_token);

Check Session sau Login

// Get current session
const { data: { session } } = await supabase.auth.getSession();

if (session) {
  console.log('User:', session.user.email);
  console.log('Expires at:', new Date(session.expires_at! * 1000));
}

Password Reset

Request Reset

const { error } = await supabase.auth.resetPasswordForEmail(
  'user@example.com',
  {
    redirectTo: 'https://myapp.com/reset-password',
  }
);

if (error) {
  console.error('Error:', error.message);
} else {
  console.log('Reset email sent');
}

Handle Reset Callback

// User clicks link → redirected to /reset-password?code=xxx

// In your reset password page
const { error } = await supabase.auth.updateUser({
  password: 'new-secure-password',
});

if (error) {
  console.error('Error updating password:', error.message);
} else {
  console.log('Password updated successfully');
}

React Form Example

Signup Form

'use client';

import { useState } from 'react';
import { createClient } from '@/lib/supabase/client';

export function SignupForm() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [loading, setLoading] = useState(false);
  const [message, setMessage] = useState('');

  const supabase = createClient();

  const handleSignup = async (e: React.FormEvent) => {
    e.preventDefault();
    setLoading(true);

    const { error } = await supabase.auth.signUp({
      email,
      password,
      options: {
        emailRedirectTo: `${location.origin}/auth/callback`,
      },
    });

    if (error) {
      setMessage(error.message);
    } else {
      setMessage('Check your email to confirm your account!');
    }

    setLoading(false);
  };

  return (
    <form onSubmit={handleSignup}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Email"
        required
      />
      <input
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
        placeholder="Password"
        minLength={6}
        required
      />
      <button type="submit" disabled={loading}>
        {loading ? 'Loading...' : 'Sign Up'}
      </button>
      {message && <p>{message}</p>}
    </form>
  );
}

Login Form

'use client';

import { useState } from 'react';
import { useRouter } from 'next/navigation';
import { createClient } from '@/lib/supabase/client';

export function LoginForm() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [error, setError] = useState('');
  const [loading, setLoading] = useState(false);

  const router = useRouter();
  const supabase = createClient();

  const handleLogin = async (e: React.FormEvent) => {
    e.preventDefault();
    setLoading(true);
    setError('');

    const { error } = await supabase.auth.signInWithPassword({
      email,
      password,
    });

    if (error) {
      setError(error.message);
      setLoading(false);
    } else {
      router.push('/dashboard');
      router.refresh();
    }
  };

  return (
    <form onSubmit={handleLogin}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Email"
        required
      />
      <input
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
        placeholder="Password"
        required
      />
      <button type="submit" disabled={loading}>
        {loading ? 'Loading...' : 'Login'}
      </button>
      {error && <p className="error">{error}</p>}
    </form>
  );
}

Auth Callback Handler

Next.js Route Handler

// app/auth/callback/route.ts
import { createClient } from '@/lib/supabase/server';
import { NextResponse } from 'next/server';

export async function GET(request: Request) {
  const { searchParams, origin } = new URL(request.url);
  const code = searchParams.get('code');
  const next = searchParams.get('next') ?? '/dashboard';

  if (code) {
    const supabase = await createClient();
    const { error } = await supabase.auth.exchangeCodeForSession(code);

    if (!error) {
      return NextResponse.redirect(`${origin}${next}`);
    }
  }

  // Return to error page
  return NextResponse.redirect(`${origin}/auth/error`);
}

Password Validation

Client-side Validation

function validatePassword(password: string): string[] {
  const errors: string[] = [];

  if (password.length < 8) {
    errors.push('Password must be at least 8 characters');
  }
  if (!/[A-Z]/.test(password)) {
    errors.push('Password must contain uppercase letter');
  }
  if (!/[a-z]/.test(password)) {
    errors.push('Password must contain lowercase letter');
  }
  if (!/[0-9]/.test(password)) {
    errors.push('Password must contain a number');
  }

  return errors;
}

Supabase Password Settings

Dashboard → Authentication → Providers → Email
- Minimum Password Length: 8 (default: 6)

Best Practices

Security Checklist

✅ Enable email confirmation
✅ Set minimum password length (≥8)
✅ Use HTTPS everywhere
✅ Implement rate limiting
✅ Hash/salt on Supabase side (automatic)
✅ Secure password reset flow
✅ Don't leak user existence info

Error Handling

// Don't reveal if email exists
const { error } = await supabase.auth.signInWithPassword({
  email,
  password,
});

// Generic message to user
if (error) {
  // Don't show: "User not found"
  // Show: "Invalid email or password"
  setError('Invalid email or password');
}

Tổng kết

Email Auth Methods

Method Purpose
signUp Create new account
signInWithPassword Login với email/password
resetPasswordForEmail Request password reset
updateUser Update password
signOut Logout

Flow Summary

Signup → Confirm Email → Login → Use App → Logout
                    Forgot Password → Reset → Login

Q&A

  1. Có custom email templates chưa?
  2. Yêu cầu password strength như thế nào?
  3. Đã implement password reset chưa?