- Cookie-based auth via backend proxy (httpOnly JWTs) - Supabase Postgres persistence for sessions/messages/profiles + RLS - Fix cross-user session leak (token cache keyed by full token, not 50-char prefix) - Fix missing table grants (42501) via migration; auto-provision profiles on user creation - Chat validation, ownership checks, rate limiting, /api/chat/sessions route ordering - Frontend auth-state reset + credentials include - OpenRouter AI provider (OpenAI-compatible base URL, reasoning disabled) - Tests: chatValidation, appState
67 lines
2.5 KiB
PL/PgSQL
67 lines
2.5 KiB
PL/PgSQL
-- Production-like user control: application-level user table (public.profiles)
|
|
-- auto-provisioned from auth.users, with admin-managed control fields.
|
|
|
|
-- 1. Control columns (inherit existing table-wide grants automatically)
|
|
ALTER TABLE public.profiles
|
|
ADD COLUMN IF NOT EXISTS role text NOT NULL DEFAULT 'user',
|
|
ADD COLUMN IF NOT EXISTS is_banned boolean NOT NULL DEFAULT false;
|
|
|
|
-- 2. Backfill profiles for existing auth users (e.g. the current test accounts)
|
|
INSERT INTO public.profiles (id, email)
|
|
SELECT id, email FROM auth.users
|
|
ON CONFLICT (id) DO NOTHING;
|
|
|
|
-- 3. Auto-provision a profile row whenever an auth user is created (signup/admin)
|
|
CREATE OR REPLACE FUNCTION public.handle_new_user()
|
|
RETURNS trigger
|
|
LANGUAGE plpgsql
|
|
SECURITY DEFINER
|
|
SET search_path = ''
|
|
AS $$
|
|
BEGIN
|
|
INSERT INTO public.profiles (id, email)
|
|
VALUES (new.id, new.email)
|
|
ON CONFLICT (id) DO NOTHING;
|
|
RETURN new;
|
|
END;
|
|
$$;
|
|
|
|
-- 4. Keep profile email in sync with auth.users email changes
|
|
CREATE OR REPLACE FUNCTION public.handle_user_email_change()
|
|
RETURNS trigger
|
|
LANGUAGE plpgsql
|
|
SECURITY DEFINER
|
|
SET search_path = ''
|
|
AS $$
|
|
BEGIN
|
|
IF new.email IS DISTINCT FROM old.email THEN
|
|
UPDATE public.profiles SET email = new.email WHERE id = new.id;
|
|
END IF;
|
|
RETURN new;
|
|
END;
|
|
$$;
|
|
|
|
DROP TRIGGER IF EXISTS on_auth_user_created ON auth.users;
|
|
CREATE TRIGGER on_auth_user_created
|
|
AFTER INSERT ON auth.users
|
|
FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();
|
|
|
|
DROP TRIGGER IF EXISTS on_auth_user_email_change ON auth.users;
|
|
CREATE TRIGGER on_auth_user_email_change
|
|
AFTER UPDATE OF email ON auth.users
|
|
FOR EACH ROW EXECUTE FUNCTION public.handle_user_email_change();
|
|
|
|
-- 5. Users may update their own profile, but must NOT be able to escalate
|
|
-- their own role or unban themselves.
|
|
-- NOTE: a column-level REVOKE does NOT override a broad TABLE-level grant.
|
|
-- The earlier grant migration gave INSERT,UPDATE,DELETE on profiles to
|
|
-- anon/authenticated (table-wide => every column). So undo the table-level
|
|
-- write grants for user-facing roles, then re-grant UPDATE only on the
|
|
-- editable columns. Profiles are created via the trigger, so users need no
|
|
-- INSERT or DELETE. service_role (backend/admin) keeps full access and
|
|
-- bypasses RLS.
|
|
REVOKE INSERT, UPDATE, DELETE ON public.profiles FROM authenticated;
|
|
REVOKE INSERT, UPDATE, DELETE ON public.profiles FROM anon;
|
|
|
|
GRANT SELECT ON public.profiles TO anon, authenticated;
|
|
GRANT UPDATE (display_name, avatar_url) ON public.profiles TO authenticated; |