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/signinwhen logged out)
The 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.

2. In Google Cloud Console
- Go to console.cloud.google.com → create a project (or pick one)
- APIs & Services → Credentials → Create Credentials → OAuth client ID
- Application type: Web application
- Authorized redirect URIs: copy the callback URL from Supabase (shown on the Google provider page — looks like
https://xxx.supabase.co/auth/v1/callback) - 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.
Walk me through enabling Google OAuth for this DirectoryKit project. I have:
- Supabase project URL: {https://xxx.supabase.co\}
- Production app URL: {https://mydirectory.com\}
- Local dev URL: http://localhost:3000
Give me a step-by-step checklist covering:
- Which callback URL to authorize in Google Cloud Console (both production and Supabase's callback).
- Which Authorized JavaScript origins to add.
- Where to paste Client ID and Client Secret in Supabase.
- Which URLs to add to Supabase Authentication → URL Configuration → Redirect URLs.
- Any .env.local or Vercel env vars I need to set.
- 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:

Making a user an admin
Two fields, both need to be set:
- In Supabase → Table Editor →
users→ find the user - Set
is_admintotrue - Set
roletoadmin
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 inClient 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()
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.