Matter-participant access model replaces implicit ownership for portal resources

MuseLegal rewrites portal authorization around an explicit many-to-many `matter_participants` join, enforced at both the database (Supabase RLS) and application tiers. This is infrastructure groundwork for a client-portal product layer sitting on top of the upstream assistant.

multi-tenantsecurity

Two commits land this: b0ce349f adds the code, 62747299 is the PR merge with no additional changes.

The migration creates public.matter_participants - roles constrained to client, attorney, reviewer, paralegal - and two SQL functions: current_portal_role() reads auth.jwt() ->> 'role', and can_access_matter() evaluates membership. Partners and admins are unconditional pass-throughs; everyone else needs a row in the join table. A DO block wires RLS policies onto matters, documents, portal_messages, tasks, intake_submissions, and downloadable_files, using to_regclass() checks so the migration skips tables that don't exist yet.

backend/src/lib/access.ts gains canAccessMatterByRole (direct participants query) and ensureMatterScopedResourceAccess (fetch resource's matter_id, then check). Both fail safe - any database error returns false.

This sits alongside, rather than replacing, upstream's project/shared_with model. Both code paths remain live, which can produce confusing dual-layer authorization if you merge this without auditing which tables each path covers.

Three things to verify before pulling this in: the JWT role claim must be injected at the Supabase Auth level (not shown here); the matters table must already exist in your schema; and the new RLS policies interact with whatever policies your fork already applies to these tables.

So what Worth pulling as a pattern if you're adding a client portal. The SQL/TypeScript mirroring is a defensible approach to defense-in-depth authorization. Don't adopt it as a drop-in unless you've confirmed the JWT claim injection, the `matters` schema, and the interaction with any existing RLS policies.

View this fork on GitHub →

Spotted something wrong? Or know the PR text has fresher detail than the writeup above?

Commits in this thread

2 commits from MuseLegal/AI-Legal-Platform, oldest first. Source extracted verbatim from the harvested git log.

SHA Subject Author Date
b0ce349f Add matter_participants-based portal access controls Griot Vault 2026-05-03 ↗ GitHub
62747299 Merge pull request #1 from MuseLegal/codex/revise-portal-permission-model-based-on-roles Griot Vault 2026-05-03 ↗ GitHub
Add many-to-many matter participant authorization model

Capture this thread into my fork

Download a single Markdown prompt that tells Claude how to port every commit above into your working tree — adapting paths and structure to match your repo. Run it via claude -p < capture-thread-98.md from inside the repo you want the changes in.

⬇ Download capture-thread-98.md