Skip to main content
8Space uses Supabase Auth for secure authentication with support for email/password and OAuth providers.

Overview

Authentication features:
  • Email/password authentication
  • Google OAuth integration
  • Automatic profile creation
  • Session persistence
  • Auto-refresh tokens
  • Row Level Security (RLS) integration

Supabase Client Configuration

The authentication client is configured in packages/app/src/integrations/supabase/client.ts:
import { createClient } from '@supabase/supabase-js';

const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;

export const supabase = createClient(
  supabaseUrl ?? fallbackUrl,
  supabaseAnonKey ?? fallbackAnonKey,
  {
    auth: {
      autoRefreshToken: true,
      persistSession: true,
      detectSessionInUrl: true,
    },
  }
);
The client includes fallback values for local development, so you can start coding without configuration.

Email Authentication

Email/password authentication is enabled by default in Supabase.

Configuration

1

Local Development

No additional configuration needed. Supabase local instance has email auth enabled.
2

Production

Configure email settings in Supabase Dashboard:
  1. Navigate to Authentication → Settings
  2. Configure Email Templates (optional)
  3. Set up SMTP settings (optional, uses Supabase’s SMTP by default)

Email Verification

By default, Supabase requires email verification. You can disable this in:
  • Local: packages/app/supabase/config.toml
  • Production: Supabase Dashboard → Authentication → Settings → Email Auth
packages/app/supabase/config.toml
[auth.email]
enable_signup = true
double_confirm_changes = true
enable_confirmations = false  # Set to false to disable email verification

Google OAuth

Google OAuth allows users to sign in with their Google account.

Setup Instructions

1

Create Google OAuth Credentials

  1. Go to Google Cloud Console
  2. Create a new project or select existing
  3. Enable Google+ API
  4. Navigate to Credentials → Create Credentials → OAuth 2.0 Client ID
  5. Set Application Type to “Web application”
2

Configure Redirect URIs

Add authorized redirect URIs:Local development:
http://127.0.0.1:54321/auth/v1/callback
Production:
https://your-project.supabase.co/auth/v1/callback
3

Add Credentials to Environment

Add to packages/landing/.env.local:
GOOGLE_CLIENT_ID=123456789-abc.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-your-secret-here
4

Configure Supabase

Local development (packages/app/supabase/config.toml):
[auth.external.google]
enabled = true
client_id = "your-client-id"
secret = "your-client-secret"
Production (Supabase Dashboard):
  1. Navigate to Authentication → Providers
  2. Enable Google
  3. Add Client ID and Client Secret
Never commit GOOGLE_CLIENT_SECRET to version control. Use environment variables or secret management.

Profile Creation

Automatic Profile Handling

When a user signs up, a trigger automatically creates their profile:
create or replace function public.handle_new_user()
returns trigger
language plpgsql
security definer
set search_path = public
as $$
begin
  insert into public.profiles (id, display_name, avatar_url)
  values (
    new.id,
    coalesce(
      new.raw_user_meta_data ->> 'full_name',
      new.raw_user_meta_data ->> 'name',
      split_part(new.email, '@', 1)
    ),
    coalesce(
      new.raw_user_meta_data ->> 'avatar_url',
      new.raw_user_meta_data ->> 'picture'
    )
  )
  on conflict (id) do update set
    display_name = coalesce(excluded.display_name, profiles.display_name),
    avatar_url = coalesce(excluded.avatar_url, profiles.avatar_url);

  return new;
end;
$$;
This trigger:
  • Extracts name from OAuth metadata (supports Google)
  • Falls back to email prefix if no name provided
  • Handles both avatar_url and picture fields
  • Updates existing profiles on conflict

Profile Fields

The profiles table stores user information:
create table public.profiles (
  id uuid primary key references auth.users (id) on delete cascade,
  display_name text not null default 'User',
  avatar_url text,
  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now()
);

Session Management

Session Persistence

Sessions are automatically persisted to localStorage:
{
  auth: {
    persistSession: true,  // Store session in localStorage
    autoRefreshToken: true, // Auto-refresh before expiry
    detectSessionInUrl: true, // Handle OAuth callbacks
  }
}

Getting Current User

const { data: { user } } = await supabase.auth.getUser();

if (user) {
  console.log('User ID:', user.id);
  console.log('Email:', user.email);
  console.log('Metadata:', user.user_metadata);
}

Auth State Changes

supabase.auth.onAuthStateChange((event, session) => {
  if (event === 'SIGNED_IN') {
    console.log('User signed in:', session.user);
  }
  if (event === 'SIGNED_OUT') {
    console.log('User signed out');
  }
});

Row Level Security

Auth Integration

RLS policies use auth.uid() to enforce permissions:
-- Users can only read their own profile
create policy "profiles_select_authenticated"
on public.profiles for select to authenticated
using (true);

-- Users can only update their own profile
create policy "profiles_update_self"
on public.profiles for update to authenticated
using (id = auth.uid())
with check (id = auth.uid());

Tenant Access

Tenant access is controlled via membership:
create policy "tenants_select_members"
on public.tenants for select to authenticated
using (public.is_tenant_member(id));
The is_tenant_member() function checks the current user’s tenant membership:
create or replace function public.is_tenant_member(p_tenant_id uuid)
returns boolean
language sql
stable
security definer
as $$
  select exists(
    select 1 from public.tenant_members
    where tenant_id = p_tenant_id
      and user_id = auth.uid()
  );
$$;

Testing Authentication

Local Test Accounts

The seed file creates test users (password: password123):

Sign In Flow

const { data, error } = await supabase.auth.signInWithPassword({
  email: '[email protected]',
  password: 'password123',
});

if (error) {
  console.error('Sign in failed:', error.message);
} else {
  console.log('Signed in:', data.user);
}

Security Best Practices

Environment Variables

Store OAuth secrets in environment variables, never in code

HTTPS Only

Always use HTTPS in production for OAuth callbacks

Row Level Security

Enable RLS on all tables to enforce access control

Token Refresh

Enable auto-refresh to prevent session expiration

Troubleshooting

Ensure redirect URI matches exactly:
http://127.0.0.1:54321/auth/v1/callback
Note: Use 127.0.0.1, not localhost (Google treats them differently).
Check that persistSession: true is set in client configuration. Clear localStorage and try again:
localStorage.clear();
window.location.reload();
Check that the trigger exists:
select * from pg_trigger where tgname = 'on_auth_user_created';
Re-run migrations if missing:
cd packages/app
supabase db reset

Additional Providers

Supabase supports many OAuth providers. To add more:
  1. Enable provider in Supabase Dashboard or config.toml
  2. Add credentials to environment variables
  3. Update the handle_new_user() function if provider uses different metadata fields
Supported providers:
  • GitHub
  • GitLab
  • Bitbucket
  • Azure
  • Facebook
  • Twitter
  • Discord
  • And more…

Next Steps

Database Setup

Understand the profile and user schema

Environment Variables

Configure all required variables

Build docs developers (and LLMs) love