Skip to main content

Authentication

Email/password + Google OAuth via Supabase Auth.

DirectoryKit uses Supabase Auth for user accounts — no external service to sign up for, no OAuth apps to wire up (except Google, if you want it).

What works out of the box

  • Email + password signup & login
  • "Forgot my password" email flow
  • Session cookies, auto-refreshed by middleware
  • Protected pages (redirect to /auth/signin when logged out)

The signin page

The /auth/signin page

Users sign up or sign in from /auth/signin. OAuth flows come back through /auth/callback.

Enabling Google OAuth (optional)

If you want the "Continue with Google" button to work:

1. In Supabase

Authentication → Providers → Google → toggle on.

Supabase Authentication Providers page

2. In Google Cloud Console

  1. Go to console.cloud.google.com → create a project (or pick one)
  2. APIs & Services → Credentials → Create Credentials → OAuth client ID
  3. Application type: Web application
  4. Authorized redirect URIs: copy the callback URL from Supabase (shown on the Google provider page — looks like https://xxx.supabase.co/auth/v1/callback)
  5. Save → copy Client ID and Client Secret

3. Back in Supabase

Paste the Client ID and Client Secret into the Google provider form → Save.

4. Restart your dev server

Done. "Continue with Google" now works.

AI Prompt· Enable Google OAuth end-to-end

Walk me through enabling Google OAuth for this DirectoryKit project. I have:

Give me a step-by-step checklist covering:

  1. Which callback URL to authorize in Google Cloud Console (both production and Supabase's callback).
  2. Which Authorized JavaScript origins to add.
  3. Where to paste Client ID and Client Secret in Supabase.
  4. Which URLs to add to Supabase Authentication → URL Configuration → Redirect URLs.
  5. Any .env.local or Vercel env vars I need to set.
  6. A quick smoke test to confirm end-to-end sign-in works.

Don't edit my code — this is Supabase/Google dashboard config only.

User dashboard

Once logged in, users get a dashboard for their submissions, profile, and settings:

User dashboard profile page

Making a user an admin

Two fields, both need to be set:

  1. In Supabase → Table Editorusers → find the user
  2. Set is_admin to true
  3. Set role to admin
Why two fields?

isAdmin() helper (server code) checks is_admin. RLS policies (database security) use role. They must match — set both.

Auth helpers in code

Server components / API routes

import {
  getServerSession,
  getCurrentUser,
  isAdmin,
  isAuthenticated,
  requireAuth,
} from '@/lib/supabase/auth-helpers';
 
await getServerSession();   // { user } or null
await getCurrentUser();     // user object or null
await isAuthenticated();    // boolean
await isAdmin();            // boolean
await requireAuth();        // returns redirect() if not logged in

Client components

import { useUser } from '@/hooks/use-user';
 
function Dashboard() {
  const { user, loading } = useUser();
 
  if (loading) return <Spinner />;
  if (!user) return <SignInPrompt />;
 
  return <div>Hello {user.email}</div>;
}

Security: getUser() vs getSession()

Use getUser() server-side

supabase.auth.getUser() verifies the token against Supabase. getSession() just reads the cookie — a malicious user can spoof that. In API routes and server components, always use getUser().

Middleware

middleware.ts runs on every request (except static files and /api/webhooks). It:

  • Refreshes the session token so users stay logged in
  • Handles locale detection when i18n is on

It does not redirect or protect routes — that's each page's job.

See also