Skip to main content

Overview

Trending Society uses Supabase Auth for authentication across all products. One login works everywhere: Dashboard, Publisher, Platform, Agency, and Store.
Authentication is handled by the @trendingsociety/auth package. See Packages for details.

Supported Providers

Email/Password

Traditional email signup with confirmation

Google OAuth

One-click Google login

Magic Link

Passwordless email login

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                      AUTHENTICATION FLOW                         │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   User Login                                                    │
│       ↓                                                         │
│   Supabase Auth (auth.users)                                    │
│       ↓                                                         │
│   JWT Token (contains user_id, tenant_id)                       │
│       ↓                                                         │
│   RLS Policies (filter data by tenant_id)                       │
│       ↓                                                         │
│   User sees only their tenant's data                            │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Implementation

Server-Side (App Router)

import { createServerClient } from '@trendingsociety/auth';
import { cookies } from 'next/headers';

export async function getUser() {
  const cookieStore = await cookies();
  const supabase = createServerClient(cookieStore);

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

Client-Side

import { useAuth } from '@trendingsociety/auth';

export function LoginButton() {
  const { signIn, signOut, user } = useAuth();

  if (user) {
    return <button onClick={signOut}>Sign Out</button>;
  }

  return <button onClick={() => signIn('google')}>Sign in with Google</button>;
}

Middleware Protection

// middleware.ts
import { createMiddlewareClient } from '@trendingsociety/auth';
import { NextResponse } from 'next/server';

export async function middleware(request) {
  const supabase = createMiddlewareClient(request);
  const { data: { session } } = await supabase.auth.getSession();

  if (!session && request.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.redirect(new URL('/login', request.url));
  }

  return NextResponse.next();
}

Multi-Tenant Context

After authentication, users are associated with a tenant:
import { getTenantId } from '@/lib/tenant/server';

export async function getData() {
  const tenantId = await getTenantId();

  // All queries automatically filtered by tenant_id via RLS
  const { data } = await supabase
    .from('content')
    .select('*');

  return data; // Only returns current tenant's data
}
Never bypass RLS in user-facing code. Use service_role only in Edge Functions and server-side admin operations.

Session Management

Supabase automatically refreshes JWT tokens. The @trendingsociety/auth package handles this seamlessly.
Sessions persist across browser restarts using secure HTTP-only cookies.
Once logged in, users can navigate between products without re-authenticating.

Google OAuth Setup

1

Google Cloud Console

Create OAuth credentials at console.cloud.google.com
2

Configure Redirect URIs

Add these URIs in Google Cloud Console:
https://ymdccxqzmhxgbjbppywf.supabase.co/auth/v1/callback
http://localhost:3001/auth/callback (for development)
3

Add to Supabase

In Supabase Dashboard > Auth > Providers > Google:
  • Add Client ID
  • Add Client Secret
4

Test

pnpm dev --filter=dashboard
# Visit localhost:3001/login
Changes to OAuth configuration take 2-3 minutes to propagate. See Known Gotchas for common issues.

Environment Variables

# Required for all apps
NEXT_PUBLIC_SUPABASE_URL=https://ymdccxqzmhxgbjbppywf.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key

# Server-side only
SUPABASE_SERVICE_ROLE_KEY=your_service_role_key
Never expose SUPABASE_SERVICE_ROLE_KEY to the client. It bypasses all RLS policies.