Domain restriction
Only Google accounts whose email address ends with@nj.sgadi.us are granted access. This is enforced by two functions in lib/admin-auth.ts:
lib/admin-auth.ts
isAdminDomainUser is called in:
app/admin/registrations/page.tsx— server component, redirects to/admin/registrations/unauthorizedon failureapp/api/admin/registrations/route.ts— returns403 Forbiddenapp/api/admin/registrations/export/route.ts— returns403 Forbiddenapp/api/admin/registrations/count/route.ts— returns403 Forbidden
Authentication flow
User visits /admin/registrations
The server component calls
supabase.auth.getUser(). If no active session is found, the sign-in page is rendered with the AdminSignIn component.User clicks 'Sign in with Google'
The client sets a short-lived
rm-auth-next cookie (10 minutes, SameSite=Lax) that stores the post-auth destination path (/admin/registrations). This cookie persists across subdomain redirects on njrajatmahotsav.com via a Domain=.njrajatmahotsav.com attribute on production.Google OAuth redirect to /auth/callback
The callback route at If the exchange fails, the user is redirected to
app/auth/callback/route.ts reads the code query parameter and exchanges it for a Supabase session:/auth/auth-code-error.Destination redirect
After a successful session exchange, the callback reads the destination from the
rm-auth-next cookie (falling back to the next query param, then /). The cookie is cleared and the user is redirected to /admin/registrations.Session refresh via middleware
The Next.js middleware (utils/supabase/middleware.ts) calls supabase.auth.getClaims() on every request to keep the Supabase session cookie fresh. For all /admin routes and /api/registrations/export, it also sets Cache-Control: no-store, max-age=0 to prevent any caching of sensitive responses.
utils/supabase/middleware.ts
Supabase client setup
Two Supabase client helpers are used:| Helper | File | Used in |
|---|---|---|
createBrowserClient | utils/supabase/client.ts | AdminSignIn (OAuth initiation, sign-out) |
createServerClient | utils/supabase/server.ts | Server components, Route Handlers (session reads) |
cookies() API. When called from a Server Component (where setAll is not permitted), cookie writes fail silently — the middleware handles the actual refresh.
Row Level Security
All registration data in Supabase is protected by Row Level Security (RLS). The API routes use the server-side Supabase client, which carries the authenticated user’s session. Queries are only permitted for users whose session satisfies the RLS policies defined on theregistrations table.
Environment variables
| Variable | Required | Description |
|---|---|---|
NEXT_PUBLIC_SUPABASE_URL | Yes | Your Supabase project URL |
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY | Yes (preferred) | Supabase anon/publishable key |
NEXT_PUBLIC_SUPABASE_ANON_KEY | Fallback | Legacy anon key name (still supported) |
NEXT_PUBLIC_SUPABASE_URL or the key is missing, utils/supabase/server.ts throws a descriptive error listing which variables are absent, and the admin page renders an error message rather than crashing silently.
Security summary
What stops a [email protected] user from accessing the API?
What stops a [email protected] user from accessing the API?
Every API route handler independently calls
isAdminDomainUser(user) after verifying the Supabase session. A missing or invalid session returns 401 Unauthorized; a valid session with the wrong email returns 403 Forbidden. The check is not delegated to middleware alone, so it cannot be bypassed by hitting the API directly.Is the domain check only on the frontend?
Is the domain check only on the frontend?
No. The server component at
app/admin/registrations/page.tsx checks the domain and issues a redirect, but this is a UX layer. The underlying API routes (/api/admin/registrations, /api/admin/registrations/export, /api/admin/registrations/count) each perform the same isAdminDomainUser check independently.What happens if Supabase is not configured?
What happens if Supabase is not configured?
utils/supabase/server.ts throws an error listing the missing environment variables. The admin page catches this and renders a descriptive message with instructions to check .env.local. No stack trace or secret is exposed to the user.Are there any hardcoded credentials?
Are there any hardcoded credentials?
No. All Supabase credentials are read exclusively from environment variables. The only hardcoded auth-related value is the
ALLOWED_DOMAIN constant (@nj.sgadi.us) in lib/admin-auth.ts, which is not a secret.