CtrlK
BlogDocsLog inGet started
Tessl Logo

g14wxz/custom-access-token-hook

Injects tenant ID and RBAC permissions into JWT via Postgres Auth Hooks during token issuance.

97

Quality

97%

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

index.mddocs/

Custom Access Token Hook

Injects tenant ID and RBAC permissions into JWT via Postgres Auth Hooks during token issuance.

Overview

This tile implements a Postgres Auth Hook that runs during Supabase token issuance. The hook queries role-mapping tables and uses jsonb_set to inject tenant_id and serialized permissions into the JWT app_metadata. Downstream RLS policies then extract these values via auth.jwt() -> 'app_metadata', eliminating the need for recursive JOINs in client queries.

Reference

Hook Function Signature

CREATE OR REPLACE FUNCTION public.custom_access_token_hook(event jsonb)
RETURNS jsonb
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
  claims jsonb;
  tenant_id uuid;
BEGIN
  claims := event->'claims';
  SELECT tm.tenant_id INTO tenant_id
    FROM tenant_members tm
    WHERE tm.user_id = (event->>'user_id')::uuid;
  claims := jsonb_set(claims, '{app_metadata, tenant_id}', to_jsonb(tenant_id));
  event := jsonb_set(event, '{claims}', claims);
  RETURN event;
END;
$$;

RLS Policy Pattern

CREATE POLICY tenant_isolation ON some_table
  USING ((auth.jwt() -> 'app_metadata' ->> 'tenant_id')::uuid = tenant_id);

Permission Grants

GRANT EXECUTE ON FUNCTION public.custom_access_token_hook TO supabase_auth_admin;
REVOKE EXECUTE ON FUNCTION public.custom_access_token_hook FROM public, anon, authenticated;

Dependencies

  • supabase-mcp-verification — Root prerequisite. MUST be installed and passing.
  • A role-mapping table (e.g., tenant_members) MUST exist before this tile is executed.

Composition Position

  • Stage: auth-foundation
  • Priority: HIGH
  • Security Critical: Yes
  • Performance Critical: Yes
  • Executes after supabase-mcp-verification. RLS policies and downstream tiles consume the injected JWT claims.

docs

index.md

tile.json