feat(auth): wire getAdminUser() to Auth.js v5 Google sign-in
Deploy to route.crispygoat.com / deploy (push) Failing after 2s
Deploy to route.crispygoat.com / deploy (push) Failing after 2s
After a user signs in with Google, they land on /admin but see
'Your account does not have admin access' because getAdminUser()
only checked the legacy dev_session and rc_auth_uid cookies.
This completes the Auth.js path:
- New src/lib/db.ts: shared pg.Pool singleton (extracted from
src/lib/auth.ts). The single connection pool for the whole app
— server actions, API routes, and Auth.js all import from here.
- src/lib/auth.ts: imports the shared pool, signIn event now calls
the new upsert_admin_user_for_authjs RPC (idempotent) to
auto-create a platform_admin row on first sign-in.
- New supabase/migrations/209_authjs_auto_create_admin.sql:
- Defensive ALTER TABLE ADD COLUMN IF NOT EXISTS for
can_manage_settings (was likely dashboard-added, not in any
tracked migration)
- SECURITY DEFINER RPC upsert_admin_user_for_authjs(p_user_id UUID)
that inserts a platform_admin row with all permissions true
and ON CONFLICT (user_id) DO NOTHING
- NOTIFY pgrst to reload PostgREST schema cache
- src/lib/admin-permissions.ts: new Auth.js session check between
dev_session and rc_auth_uid. Uses auth() from @/lib/auth to
decrypt the JWT cookie server-side, then getAdminUserFromPool()
queries admin_users + admin_user_brands via the shared pool.
Legacy rc_auth_uid path unchanged (deferred).
- src/middleware.ts: recognizes Auth.js session cookies
(authjs.session-token and __Secure-authjs.session-token) at the
edge so signed-in users aren't bounced to /login.
Flow after this change:
Dev/demo: visit /admin → middleware auto-issues dev_session → in
Prod: click Google → Auth.js OAuth → signIn event creates
admin_users row → redirect to /admin → getAdminUser()
reads JWT, queries pool, returns platform_admin.
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
-- 209_authjs_auto_create_admin.sql
|
||||
-- Auto-create a platform_admin row when a new user signs in via Auth.js.
|
||||
--
|
||||
-- Called from the `signIn` event in `src/lib/auth.ts`. The RPC is
|
||||
-- idempotent (ON CONFLICT DO NOTHING) so repeat sign-ins are no-ops.
|
||||
|
||||
-- Defensive: ensure can_manage_settings column exists. It was likely
|
||||
-- added via the Supabase dashboard (it's referenced in the TypeScript
|
||||
-- `AdminUser` type at `src/lib/admin-permissions-types.ts` but not in
|
||||
-- any tracked migration). ADD COLUMN IF NOT EXISTS is safe to re-run.
|
||||
ALTER TABLE admin_users
|
||||
ADD COLUMN IF NOT EXISTS can_manage_settings BOOLEAN NOT NULL DEFAULT false;
|
||||
|
||||
-- SECURITY DEFINER RPC: upsert a platform_admin row for the given
|
||||
-- Auth.js user id.
|
||||
--
|
||||
-- Bypasses RLS on admin_users (which is enabled — see
|
||||
-- 109_enable_rls_critical.sql:21). Runs with the function owner's
|
||||
-- privileges so the auto-create on first sign-in can always succeed.
|
||||
CREATE OR REPLACE FUNCTION upsert_admin_user_for_authjs(p_user_id UUID)
|
||||
RETURNS SETOF admin_users
|
||||
LANGUAGE plpgsql
|
||||
SECURITY DEFINER
|
||||
SET search_path = public
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
INSERT INTO admin_users (
|
||||
user_id,
|
||||
role,
|
||||
active,
|
||||
must_change_password,
|
||||
can_manage_products,
|
||||
can_manage_stops,
|
||||
can_manage_orders,
|
||||
can_manage_pickup,
|
||||
can_manage_messages,
|
||||
can_manage_refunds,
|
||||
can_manage_users,
|
||||
can_manage_water_log,
|
||||
can_manage_reports,
|
||||
can_manage_settings
|
||||
)
|
||||
VALUES (
|
||||
p_user_id,
|
||||
'platform_admin',
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
)
|
||||
ON CONFLICT (user_id) DO NOTHING
|
||||
RETURNING *;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- Reload PostgREST schema cache so the new RPC is immediately callable.
|
||||
NOTIFY pgrst, 'reload schema';
|
||||
Reference in New Issue
Block a user