This guide will walk you through the process of creating effective RLS policies using KeyHippo.

Understanding KeyHippo in RLS

KeyHippo extends Supabase’s RLS with two primary functions:

  1. auth.keyhippo_check(user_id UUID): Verifies if the request is authenticated with a valid API key for the given user.
  2. keyhippo.key_uid(): Retrieves the user ID associated with the current API key.

These functions allow you to create policies that seamlessly handle both traditional session-based auth and API key auth.

Basic Policy Structure

A typical KeyHippo-enabled RLS policy follows this structure:

CREATE POLICY "policy_name"
ON schema_name.table_name
FOR operation
TO role
USING (
  -- Session-based auth check
  (auth.uid() = column_name)
  OR
  -- API key auth check
  (auth.keyhippo_check(column_name))
);

Example Policies

1. Basic Read Access Policy

This policy allows users to read their own data, whether authenticated by session or API key:

CREATE POLICY "users_read_own_data"
ON public.user_data
FOR SELECT
USING (
  auth.uid() = user_id
  OR auth.keyhippo_check(user_id)
);

2. Write Access Policy

This policy allows users to insert or update their own data:

CREATE POLICY "users_write_own_data"
ON public.user_data
FOR INSERT
WITH CHECK (
  auth.uid() = user_id
  OR auth.keyhippo_check(user_id)
);

CREATE POLICY "users_update_own_data"
ON public.user_data
FOR UPDATE
USING (
  auth.uid() = user_id
  OR auth.keyhippo_check(user_id)
);

3. Role-Based Access Control

This policy allows admin users to access all data:

CREATE POLICY "admin_access_all_data"
ON public.sensitive_data
FOR ALL
USING (
  (auth.uid() IN (SELECT user_id FROM user_roles WHERE role = 'admin'))
  OR
  (auth.keyhippo_check(auth.uid()) AND
   auth.uid() IN (SELECT user_id FROM user_roles WHERE role = 'admin'))
);

4. API Key-Specific Permissions

This policy allows access based on specific API key permissions:

CREATE POLICY "premium_api_access"
ON public.premium_content
FOR SELECT
USING (
  auth.uid() IN (SELECT user_id FROM premium_users)
  OR (
    keyhippo.key_uid() IS NOT NULL
    AND keyhippo.key_uid() IN (SELECT user_id FROM premium_users)
    AND EXISTS (
      SELECT 1
      FROM keyhippo.get_api_key_metadata(keyhippo.key_uid()) AS key_meta
      WHERE key_meta.permission = 'premium'
    )
  )
);

Best Practices

  1. Consistency: Ensure policies treat session-based and API key authentication consistently where appropriate.

  2. Granularity: Create separate policies for different operations (SELECT, INSERT, UPDATE, DELETE) rather than using catch-all policies.

  3. Performance: Be mindful of policy complexity. Overly complex policies can impact query performance.

  4. Testing: Thoroughly test your policies with both authentication methods to ensure they behave as expected.

  5. Documentation: Maintain clear documentation of your RLS policies, including the reasoning behind each policy.

Advanced Techniques

Combining Multiple Conditions

You can create more complex policies by combining multiple conditions:

CREATE POLICY "complex_access_policy"
ON public.projects
FOR SELECT
USING (
  (auth.uid() = projects.owner_id OR auth.keyhippo_check(projects.owner_id))
  AND
  (projects.status = 'public' OR projects.visibility_level >=
    (SELECT user_level FROM user_levels WHERE user_id = auth.uid()))
);

Using KeyHippo Metadata

Leverage KeyHippo’s metadata functions for more sophisticated policies:

CREATE POLICY "rate_limited_access"
ON public.api_resources
FOR SELECT
USING (
  auth.uid() IS NOT NULL
  OR (
    keyhippo.key_uid() IS NOT NULL
    AND (
      SELECT total_uses
      FROM keyhippo.get_api_key_metadata(keyhippo.key_uid()) AS key_meta
    ) < 1000  -- Rate limit to 1000 uses
  )
);

Troubleshooting

If your policies aren’t working as expected:

  1. Verify KeyHippo Installation: Ensure the KeyHippo extension is properly installed and up to date.
  2. Check Permissions: Verify that the necessary permissions have been granted to the authenticated and anon roles.
  3. Test Individually: Test session-based and API key authentication separately to isolate issues.
  4. Use Supabase Logs: Utilize Supabase’s logging features to debug policy evaluation.

Next Steps

With your RLS policies in place, you can now:

  1. Implement API key authentication in your client application
  2. Test your policies with various authentication scenarios
  3. Monitor and analyze API key usage patterns

Manage API Keys

Learn how to effectively manage API keys throughout their lifecycle.