Fix Supabase RLS issues - handles missing user context, system processes, background jobs, and provides standardized RLS templates
/plugin marketplace add Primadetaautomation/claude-dev-toolkit/plugin install claude-dev-toolkit@primadata-marketplaceThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Activate this skill when you encounter:
If you get: ERROR: relation "user_tenants" does not exist
Create compatibility view:
-- Run in Supabase SQL Editor
DROP VIEW IF EXISTS user_tenants CASCADE;
CREATE OR REPLACE VIEW user_tenants AS
SELECT * FROM tenant_memberships;
GRANT SELECT ON user_tenants TO authenticated;
GRANT SELECT ON user_tenants TO anon;
If background jobs fail with "Tenant or user not found":
Update the function to handle system processes:
CREATE OR REPLACE FUNCTION get_current_user_tenant_ids()
RETURNS UUID[] AS $$
DECLARE
current_user_id_str TEXT;
current_user_uuid UUID;
tenant_ids UUID[];
BEGIN
-- Get current user setting (may be null for system processes)
current_user_id_str := current_setting('app.current_user_id', true);
-- If no user context (system process/background job), return empty array
IF current_user_id_str IS NULL OR current_user_id_str = '' THEN
RETURN ARRAY[]::UUID[];
END IF;
-- Try to convert to UUID
BEGIN
current_user_uuid := current_user_id_str::UUID;
EXCEPTION WHEN OTHERS THEN
RETURN ARRAY[]::UUID[];
END;
-- Get user's tenant IDs
SELECT ARRAY_AGG(tenant_id) INTO tenant_ids
FROM tenant_memberships
WHERE user_id = current_user_uuid;
RETURN COALESCE(tenant_ids, ARRAY[]::UUID[]);
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- Helper 1: Detect system processes
CREATE OR REPLACE FUNCTION is_system_process()
RETURNS BOOLEAN AS $$
BEGIN
-- Check if explicitly marked as system process
IF current_setting('app.is_system', true) = 'true' THEN
RETURN true;
END IF;
-- Check if no user context (also system process)
IF current_setting('app.current_user_id', true) IS NULL OR
current_setting('app.current_user_id', true) = '' THEN
RETURN true;
END IF;
RETURN false;
END;
$$ LANGUAGE plpgsql IMMUTABLE SECURITY DEFINER;
-- Helper 2: Universal access checker
CREATE OR REPLACE FUNCTION can_access_row(
p_user_id UUID DEFAULT NULL,
p_tenant_id UUID DEFAULT NULL
)
RETURNS BOOLEAN AS $$
DECLARE
v_current_user_id UUID;
v_current_tenant_id UUID;
BEGIN
-- System processes always have access
IF is_system_process() THEN
RETURN true;
END IF;
-- Get current user/tenant from session
BEGIN
v_current_user_id := current_setting('app.current_user_id', true)::UUID;
EXCEPTION WHEN OTHERS THEN
v_current_user_id := NULL;
END;
BEGIN
v_current_tenant_id := current_setting('app.current_tenant_id', true)::UUID;
EXCEPTION WHEN OTHERS THEN
v_current_tenant_id := NULL;
END;
-- Check user match
IF p_user_id IS NOT NULL AND v_current_user_id = p_user_id THEN
RETURN true;
END IF;
-- Check tenant match
IF p_tenant_id IS NOT NULL AND v_current_tenant_id = p_tenant_id THEN
RETURN true;
END IF;
-- Check if user belongs to tenant
IF p_tenant_id IS NOT NULL AND v_current_user_id IS NOT NULL THEN
RETURN EXISTS (
SELECT 1 FROM tenant_memberships
WHERE user_id = v_current_user_id
AND tenant_id = p_tenant_id
AND status = 'ACTIVE'
);
END IF;
RETURN false;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- COPY THIS TEMPLATE FOR EVERY NEW TABLE WITH RLS:
ALTER TABLE your_table_name ENABLE ROW LEVEL SECURITY;
-- SELECT: System processes + users can read
CREATE POLICY "your_table_name_select" ON your_table_name
FOR SELECT USING (
can_access_row(user_id, tenant_id)
);
-- INSERT: Only users (no system processes)
CREATE POLICY "your_table_name_insert" ON your_table_name
FOR INSERT WITH CHECK (
NOT is_system_process() AND
can_access_row(user_id, tenant_id)
);
-- UPDATE: System processes + users
CREATE POLICY "your_table_name_update" ON your_table_name
FOR UPDATE USING (
can_access_row(user_id, tenant_id)
);
-- DELETE: Only users
CREATE POLICY "your_table_name_delete" ON your_table_name
FOR DELETE USING (
NOT is_system_process() AND
can_access_row(user_id, tenant_id)
);
-- Am I a system process?
SELECT is_system_process();
-- What's my current user ID?
SELECT current_setting('app.current_user_id', true);
-- What's my current tenant ID?
SELECT current_setting('app.current_tenant_id', true);
-- What tenants do I have access to?
SELECT get_current_user_tenant_ids();
-- Can I access a specific row?
SELECT can_access_row('user-uuid-here'::UUID, 'tenant-uuid-here'::UUID);
-- Which rows can I see in a table?
SELECT * FROM your_table WHERE can_access_row(user_id, tenant_id);
-- Mark as system process before running job
SELECT set_config('app.is_system', 'true', false);
-- Or set specific user context
SELECT set_config('app.current_user_id', 'user-uuid-here', false);
SELECT set_config('app.current_tenant_id', 'tenant-uuid-here', false);
-- Table with tenant_id column
CREATE POLICY "table_tenant_policy" ON table_name
FOR ALL USING (
is_system_process() OR
tenant_id = ANY(get_current_user_tenant_ids())
);
-- Table with user_id column
CREATE POLICY "table_user_policy" ON table_name
FOR ALL USING (
is_system_process() OR
user_id = current_setting('app.current_user_id')::UUID
);
-- Anyone can read
CREATE POLICY "table_public_read" ON table_name
FOR SELECT USING (true);
-- Only authenticated users can write
CREATE POLICY "table_auth_write" ON table_name
FOR INSERT WITH CHECK (
current_setting('app.current_user_id', true) IS NOT NULL
);
When adding RLS to a table, test:
https://supabase.com/dashboard/project/[PROJECT_ID]/sql/newcurrent_setting() directly in policies → Use helper functions insteadis_system_process()can_access_row() for clarity42P01: Relation does not exist42501: Insufficient privileges22P02: Invalid text representation (bad UUID)This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.