Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/aluxey/E-Commerce/llms.txt

Use this file to discover all available pages before exploring further.

Overview

Sabbels Handmade uses Supabase Authentication to provide secure user management with role-based access control. The system supports two user roles: client (default) and admin, with Row Level Security (RLS) policies enforcing access permissions at the database level.

User Roles

The platform implements a simple but effective two-tier role system:
New users are automatically assigned the client role upon registration. Admin privileges must be granted manually through the database.

Client Role

Default role for all registered users with the following permissions:
  • View all public product data (items, categories, colors, variants)
  • Create and manage their own orders
  • View their own order history
  • Submit product ratings and reviews
  • Update their own user profile

Admin Role

Elevated permissions for shop administrators:
  • Full CRUD access to products, categories, and inventory
  • View and manage all orders from all customers
  • Access payment and webhook data
  • Manage product images in Storage
  • Update customer photo wall
  • Delete or modify any user data

Authentication Flow

Session Management

The AuthContext provider manages authentication state throughout the application:
import { createContext, useContext, useEffect, useState } from 'react';
import { supabase } from '../supabase/supabaseClient';

const AuthContext = createContext();

export const AuthProvider = ({ children }) => {
  const [session, setSession] = useState(null);
  const [userData, setUserData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    // Get initial session
    supabase.auth.getSession().then(({ data }) => {
      setSession(data.session);
      if (data.session) {
        fetchUserData(data.session.user);
      }
    });

    // Listen for auth changes
    const { data: listener } = supabase.auth.onAuthStateChange((_event, session) => {
      setSession(session);
      if (session) {
        fetchUserData(session.user);
      } else {
        setUserData(null);
      }
    });

    return () => listener?.subscription.unsubscribe();
  }, []);

  return (
    <AuthContext.Provider value={{ session, userData, loading }}>
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => useContext(AuthContext);

User Profile Creation

When a user authenticates for the first time, their profile is automatically created in the users table:
const fetchUserData = async (user) => {
  const { data, error } = await supabase
    .from('users')
    .select('id, email, role')
    .eq('id', user.id)
    .maybeSingle();

  if (!data) {
    // Create new user profile with default 'client' role
    await supabase
      .from('users')
      .upsert({ id: user.id, email: user.email, role: 'client' })
      .select('id, email, role')
      .single();
  }
};

Row Level Security (RLS)

All database tables have RLS enabled to enforce access control at the database layer. This provides security even if client-side validation is bypassed.

Admin Helper Function

A SQL security definer function checks admin status:
create or replace function public.is_admin(uid uuid)
returns boolean
language sql
stable
security definer as $$
  select exists (
    select 1 from public.users u 
    where u.id = uid and u.role = 'admin'
  );
$$;

Example RLS Policies

-- Users can read their own profile or admins can read all
create policy "Users: select own or admin" on public.users for select
  to authenticated 
  using ( id = auth.uid() or public.is_admin(auth.uid()) );

-- Users can update their own profile or admins can update any
create policy "Users: update self or admin" on public.users for update
  to authenticated 
  using ( id = auth.uid() or public.is_admin(auth.uid()) );

Storage Policies

The product-images storage bucket follows the same pattern:
  • Public read access: Anyone can view product images
  • Admin-only write: Only admins can upload, update, or delete images
create policy "Storage: read product-images"
  on storage.objects for select
  to anon, authenticated
  using ( bucket_id = 'product-images' );

create policy "Storage: insert product-images (admin)"
  on storage.objects for insert
  to authenticated
  with check ( bucket_id = 'product-images' and public.is_admin(auth.uid()) );

Using Authentication in Components

Access the current user’s authentication state using the useAuth hook:
import { useAuth } from './context/AuthContext';

function MyComponent() {
  const { session, userData, loading } = useAuth();

  if (loading) return <div>Loading...</div>;

  if (!session) {
    return <div>Please log in</div>;
  }

  return (
    <div>
      <p>Welcome, {userData?.email}</p>
      {userData?.role === 'admin' && (
        <button>Admin Panel</button>
      )}
    </div>
  );
}

API Authentication

The backend API validates requests using Bearer tokens:
server.js:146
async function getUserFromAuthHeader(authHeader) {
  if (!authHeader) return null;
  const token = authHeader.replace('Bearer ', '');
  const { data, error } = await supabase.auth.getUser(token);
  if (error) return null;
  return data.user;
}
Example usage in a protected endpoint:
app.post('/api/checkout', async (req, res) => {
  const user = await getUserFromAuthHeader(req.headers.authorization);
  if (!user) return res.status(401).json({ error: 'Unauthorized' });
  
  // Process checkout...
});
The API uses the Supabase service role key for server-side operations, which bypasses RLS. Always validate user permissions in your API endpoints.

Build docs developers (and LLMs) love