Skip to content

feat(auth): add Supabase org authz profile#2262

Draft
Sanderhoff-alt wants to merge 1 commit into
vectorize-io:mainfrom
Sanderhoff-alt:feature/supabase-org-authz-profile
Draft

feat(auth): add Supabase org authz profile#2262
Sanderhoff-alt wants to merge 1 commit into
vectorize-io:mainfrom
Sanderhoff-alt:feature/supabase-org-authz-profile

Conversation

@Sanderhoff-alt

@Sanderhoff-alt Sanderhoff-alt commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Closes #2235.

This is a first implementation slice and does not close the full issue.

Summary

This PR adds an opt-in Supabase-backed organization authorization profile for OSS deployments that want a Hindsight Cloud-like team model without changing the existing default, fixed API key, or per-user Supabase tenant modes.

The new supabase_org profile models an organization as the Hindsight tenant. A user can belong to multiple organizations, and the selected organization maps to the tenant schema used by the dataplane. The implementation adds built-in dataplane extensions, Supabase-backed policy resolution with request-context policy reuse, control-plane auth/session plumbing, organization switching and rename, team management, manual invite links, and Hindsight scoped API key management.

What Changed

Dataplane

  • Add SupabaseOrgTenantExtension for resolving either a Supabase JWT plus X-Hindsight-Org-Id, or a Hindsight scoped API key, to an organization tenant schema.
  • Add SupabaseAuthorizationExtension for enforcing organization roles, operation scopes, and bank-scoped API keys through the existing OperationValidatorExtension interface.
  • Add SupabasePolicyResolver for credential parsing, Supabase lookups, and caller policy construction. The tenant extension stores the resolved CallerPolicy on RequestContext, and the authorization extension reuses it before falling back to another resolve.
  • Add HINDSIGHT_API_AUTHZ_PROFILE=supabase_org profile validation so partial deployments fail fast instead of running with only tenant resolution or only operation authorization enabled.
  • Initialize tenant and operation validator extensions before startup migrations, and run organization schema migrations on first schema access for organizations created after API startup.
  • Extend version feature reporting with the active authz profile, extension names, and supabase_org_ready so the control plane can detect mismatched deployments.

Control Plane

  • Add HINDSIGHT_CP_AUTH_PROVIDER=supabase_org alongside the existing disabled and access-key modes.
  • Add Supabase email/password login and signup, selected-organization cookies, logout, organization switching, and /api/me.
  • Add organization, team, invite, and API key wrapper APIs backed by Supabase service-role access.
  • Add owner-only organization rename support through PATCH /api/organizations/:id.
  • Add a Settings page for organization switching, owner-only organization rename, team management, manual invite link creation, and Hindsight scoped API key management.
  • Update control-plane API routes to create request-scoped dataplane clients so supabase_org mode forwards the caller's Supabase JWT and selected organization instead of using a global dataplane client.
  • Keep fixed HINDSIGHT_CP_DATAPLANE_API_KEY behavior for existing access-key and disabled deployments.

Supabase Metadata

  • Add a local Supabase project and migration for organizations, organization_members, organization_invites, hindsight_api_keys, and hindsight_api_key_bank_scopes.
  • Enable RLS on the authorization metadata tables and define no anon/authenticated policies. Browser clients must go through the control-plane wrapper APIs; server-side code uses the service role key.

Tests and CI

  • Add backend unit tests for the resolver, tenant extension schema lifecycle, request-context policy reuse, and authorization extension behavior.
  • Add control-plane tests for auth provider behavior, request-scoped dataplane credentials, Supabase organization store behavior, and a route scan that prevents app API routes from importing global dataplane clients.
  • Add a Supabase CLI integration test covering real Auth, PostgREST, RLS behavior, JWT resolution, and scoped API key resolution.
  • Update the integration workflow to install a pinned Supabase CLI and run the Supabase organization authorization integration test.

Current V1 Boundaries

This PR intentionally keeps the first version at organization, role, Bank, and API key scope. It does not add memory-level ACLs, row-level memory authorization, cross-tenant sharing, billing or usage metering, or enterprise IAM/SSO.

The current V1 behavior is:

  • Manual invite links only. No automatic invite email is sent.
  • No password recovery UI yet, although Supabase Auth can support that in a follow-up.
  • Organization rename is supported for the selected organization's owner.
  • Organization deletion is not exposed. There is no DELETE /api/organizations/:id route or deletion UI because deleting an organization requires coordinated Hindsight tenant schema cleanup.
  • No child API keys or /api/api-keys/scoped implementation.
  • Bank scope is enforced for Hindsight scoped API keys. Owner/admin users are not restricted to a subset of banks in V1.
  • HINDSIGHT_AUTH_ORG_CREATION_POLICY=direct_signup_only allows default organization creation during direct signup but rejects post-login organization creation through POST /api/organizations.
  • Supabase authorization tables are stored in Supabase/Postgres, not in Hindsight tenant schemas.

Compatibility

The feature is fully opt-in. Existing deployments continue to use the same behavior unless they explicitly configure the new profile.

Existing modes remain separate:

  • No tenant extension: default public schema behavior.
  • ApiKeyTenantExtension: fixed API key, single schema behavior.
  • SupabaseTenantExtension: existing Supabase user-to-schema behavior.
  • supabase_org: new organization/team authorization profile requiring both SupabaseOrgTenantExtension and SupabaseAuthorizationExtension.

A complete supabase_org deployment configures both dataplane and control plane pieces, for example:

HINDSIGHT_API_AUTHZ_PROFILE=supabase_org
HINDSIGHT_API_TENANT_EXTENSION=hindsight_api.extensions.builtin.supabase_org:SupabaseOrgTenantExtension
HINDSIGHT_API_OPERATION_VALIDATOR_EXTENSION=hindsight_api.extensions.builtin.supabase_org:SupabaseAuthorizationExtension

HINDSIGHT_CP_AUTH_PROVIDER=supabase_org

HINDSIGHT_AUTH_SUPABASE_URL=...
HINDSIGHT_AUTH_SUPABASE_SERVICE_KEY=...
HINDSIGHT_AUTH_SUPABASE_ANON_KEY=...
HINDSIGHT_AUTH_PUBLIC_BASE_URL=...
HINDSIGHT_AUTH_ORG_CREATION_POLICY=direct_signup_only

Validation

Validated locally with:

cd hindsight-api-slim
uv run pytest tests/test_supabase_org_authz.py -q

cd hindsight-control-plane
npm test -- --run tests/lib/auth/provider.test.ts tests/lib/hindsight-client-auth.test.ts tests/lib/dataplane-route-auth.test.ts tests/lib/supabase-org/store.test.ts
npx tsc --noEmit -p tsconfig.json
npm run i18n:check --workspace=hindsight-control-plane

cd hindsight-integration-tests
uv run pytest tests/test_supabase_org_authz.py -v

./scripts/hooks/lint.sh
git diff --check

Pre-commit lint passed during commit creation. The unused-code advisory reported an environment issue while resolving typescript from the npx cache, but it did not block the commit.

@Sanderhoff-alt Sanderhoff-alt force-pushed the feature/supabase-org-authz-profile branch 8 times, most recently from ade3980 to 7195664 Compare June 17, 2026 12:40
Introduce an opt-in supabase_org deployment profile that models an
organization as the Hindsight tenant. The dataplane now has built-in
SupabaseOrgTenantExtension and SupabaseAuthorizationExtension classes
sharing a policy resolver for Supabase JWTs and Hindsight scoped API
keys. The resolver maps callers to organization schemas, member roles,
bank scopes, operation scopes, and tenant config without changing the
existing default, API key, or user-level Supabase tenant modes.

Wire profile validation into API startup, uvicorn import startup, and
worker startup so partial supabase_org deployments fail fast. Expose
authz profile details through version features so the control plane can
detect mismatched dataplane configuration instead of silently entering a
half-working state.

Add the control-plane supabase_org auth provider with login, signup,
logout, selected-organization cookies, organization switching, team
management, manual invite links, and Hindsight scoped API key
management. Existing API wrappers now create request-scoped dataplane
clients, forwarding the user's Supabase JWT and X-Hindsight-Org-Id in
supabase_org mode while preserving fixed dataplane API key behavior for
access_key and disabled modes.

Add Supabase local-stack config and migrations for organizations,
memberships, invites, API keys, and bank scopes. These authorization
metadata tables live in public for PostgREST access but enable RLS and
define no anon/authenticated policies, so browser clients must go
through the control-plane wrapper APIs while server-side code uses the
service role key.

Cover the implementation with resolver and extension unit tests,
control-plane auth/store/header-forwarding tests, a route scan that
prevents app API routes from importing global dataplane clients, and a
Supabase CLI integration test that exercises real Auth, PostgREST,
RLS, JWT resolution, and scoped API key resolution. The integration CI
job now installs a pinned Supabase CLI and also runs when control-plane
changes touch the local Supabase project.

Document the implemented V1 boundaries in code and tests: manual
invites only, no password recovery UI, no organization schema deletion
flow, no child API keys, no memory-level ACLs, and no cross-tenant
sharing.
@Sanderhoff-alt Sanderhoff-alt force-pushed the feature/supabase-org-authz-profile branch from 7195664 to 414446b Compare June 17, 2026 12:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: Team, agent, and memory-level access control

1 participant