Skip to main content

Google SSO setup (admin sign-in)

Admin sign-in uses Google, restricted to the nightingalepm.com Google Workspace. This is the one part of Step 2 that must be done in the Google and Supabase dashboards. The app already enforces the domain server-side in the OAuth callback, so even if the Google hint is bypassed, non-@nightingalepm.com accounts are rejected.

Project ref: dksjqoknmwpxfqkiygkb · Supabase URL: https://dksjqoknmwpxfqkiygkb.supabase.co

1. Google Cloud — OAuth client

  1. Go to https://console.cloud.google.com/ and select (or create) a project for Nightingale.
  2. APIs & Services → OAuth consent screen:
    • User type: Internal (this alone restricts sign-in to your Workspace; keep it Internal).
    • App name: "Nightingale Process Management", support email: your Workspace email.
    • Save.
  3. APIs & Services → Credentials → Create credentials → OAuth client ID:
    • Application type: Web application.
    • Name: "Nightingale Web".
    • Authorized redirect URIs — add the full callback path, exactly:
      • https://dksjqoknmwpxfqkiygkb.supabase.co/auth/v1/callback
      • ⚠️ Must include /auth/v1/callback. The bare domain (https://dksjqoknmwpxfqkiygkb.supabase.co) does not match — Google compares redirect URIs as exact strings and will reject with Error 400: redirect_uri_mismatch.
    • Create, then copy the Client ID and Client secret.

Note — two OAuth clients in this project. The Nightingale GCP project also has a separate client for Gmail inbox reading ("NPM OAuth client", ID …em7k…). SSO uses a different client ("NPM Admin SSO", ID …6pss…). Make sure the redirect URI above is on the Admin SSO client, and that Supabase (step 2) gets the Admin SSO Client ID/secret — not the Gmail one.

2. Supabase — enable the Google provider

  1. Dashboard → your project → Authentication → Providers → Google.
  2. Toggle Enable, paste the Client ID and Client secret from step 1, and save.

3. Supabase — URL configuration

Dashboard → Authentication → URL Configuration:

  • Site URL: http://localhost:3000 (for local dev; set to the production URL when deployed).
  • Redirect URLs — add:
    • http://localhost:3000/api/auth/callback
    • (later) https://<your-production-domain>/api/auth/callback

4. Local env

.env.local already has NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY, and ADMIN_SSO_ALLOWED_DOMAIN=nightingalepm.com. No Google client secret is needed in the app — it lives in Supabase (Supabase performs the token exchange). Optionally set GOOGLE_SSO_CLIENT_ID / GOOGLE_SSO_CLIENT_SECRET in .env.local for reference only.

5. Try it

  1. pnpm dev, open http://localhost:3000/login.
  2. Click Sign in with Google, choose your @nightingalepm.com account.
  3. You should land on /admin. The first admin to sign in becomes the owner; later admins are staff.
  4. A non-@nightingalepm.com account is bounced back to /login with a clear error.

Troubleshooting

SymptomCause / fix
Error 400: redirect_uri_mismatch (no account picker appears)The redirect URI registered on the Admin SSO client is wrong or incomplete — most commonly the bare domain instead of the full …supabase.co/auth/v1/callback. Google validates the URI before showing the picker, so this blocks everything. Fix the URI, save, wait ~1–5 min to propagate.
Same error, but the URI looks correctYou edited the wrong client (Gmail …em7k… instead of Admin SSO …6pss…), or Supabase has the wrong Client ID. Confirm both point at …6pss…. Also check for a trailing slash/space or http://.
Dev server came up on :3001, login redirect rejectedSomething else holds :3000. The redirect allow-list (step 3) is set for :3000, so free that port (lsof -i :3000) and restart, or add the :3001 URLs to Supabase.
"No option to sign in with my account" / account is hiddenExpected. The hd: nightingalepm.com hint hides non-Workspace accounts. Sign in with a @nightingalepm.com account (use Use another account if needed). A personal/other-domain Google account cannot sign in by design.

How it works (for reference)

  • Login button → supabase.auth.signInWithOAuth({ provider: 'google', hd: 'nightingalepm.com' }).
  • Google → Supabase callback (/auth/v1/callback) → app callback /api/auth/callback.
  • The callback verifies the email domain (src/server/auth/domain.ts), creates the profile if needed (profile.ts), and records the session (session.ts).
  • src/proxy.ts gates /admin, refreshes the session, and enforces force-logout (a revoked sessions row) and the per-role absolute timeout.