Add Teams & Workspaces to Any Web App With Supabase
Let users create workspaces, invite teammates, and collaborate safely while Supabase RLS keeps every workspace's data isolated.
Roadmap & Resources
Define Your Workspace Setup
A workspace is where one group works together in your app — a team, a company, a client or a project. Each member has a role inside it, and roles belong to the workspace, so one person can have a different role in each:
One person, three workspaces
Owner in Workspace A
Invites people, changes roles and manages settings.
Member in Workspace B
Works with the data the team shares.
Viewer in Workspace C
Can look, but not change anything.
Use your own names for workspaces and roles. This is your app's own team system, stored in your database — not the Organizations feature in the Supabase Dashboard, which only controls who can manage your Supabase projects.
Plan Your Workspace Setup
Plan Your Workspace Setup
Describe how teams should work in your app and let AI turn it into a clear workspace plan and permission matrix before anything is built.
I want to add teams and workspaces to this existing web app, so people can work together in shared workspaces. The app already uses Supabase Auth for sign-in. What the app does: {{APP_PURPOSE}} What a workspace represents: {{WORKSPACE_TYPE}} What it is called in the app: {{WORKSPACE_NAME}} Whether one person can belong to more than one: {{MULTIPLE_WORKSPACES}} Who can create a workspace: {{WHO_CREATES}} Workspace roles: {{ROLES}} What each role can do: {{ROLE_PERMISSIONS}} Role the creator of a workspace gets: {{CREATOR_ROLE}} Default role for invited members: {{INVITED_ROLE}} Who can invite members: {{WHO_INVITES}} Who can remove members: {{WHO_REMOVES}} Who can change members' roles: {{WHO_CHANGES_ROLES}} Do not change any files, and do not change the database. This task only plans how teams will work. Look through the project only enough to use its real names — the actual pages, features and data, such as projects, clients or tasks — so the plan describes this app rather than a generic template. Do not audit the code or the database yet. That is the next step. Two rules shape the plan: - These workspaces are the app's own team system, stored in the app's database. They have nothing to do with Organizations in the Supabase Dashboard, which only control who can manage Supabase projects. - A workspace role applies inside one workspace, so the same person can have a different role in each workspace they belong to. Keep workspace roles separate from any app-wide role, such as a platform admin, and give app-wide roles no access to workspace data unless I ask for it. Turn my answers into a short workspace plan: 1. Workspace: what it represents, what it is called in the interface, and whether one person can belong to several 2. Roles: each workspace role, with one sentence on who it is for 3. Permission matrix: a table with one row per action and one column per role. Include inviting members, removing members, changing roles, renaming and deleting the workspace, and the app's own actions on shared data, named after this app's real features. 4. Joining: how a workspace is created, the role its creator gets, how people join by invitation, and the role invited members get by default 5. Guardrails: every workspace always keeps at least one member with full control, and nobody can give themselves a higher role Rules: - If I named roles, use my names exactly. Do not rename them or add roles I did not ask for. If something essential seems to be missing, ask me first. - Give every role the least access that still lets it do its job. - Invited members must never get the creator's role by default. - If my answers conflict or leave a gap, ask me instead of guessing. Finish with the complete plan in a compact form I can copy and keep. Then wait for my approval. Do not implement anything.
What does your web app do?
A project tracker where agencies manage client projects, tasks and files...
What should a workspace represent?
What should it be called in your app?
Workspace
Can one person belong to more than one?
Can users create their own workspace?
Which workspace roles do you need?
What can each role do?
Owner: everything, including deleting the workspace. Admin: manage members and all records. Member: create and edit records. Viewer: view only.
Which role does the creator of a workspace get?
Owner
Which role do invited members get by default?
Member
Who can invite members?
Owner and Admin
Who can remove members?
Owner and Admin
Who can change members' roles?
Owner only
Adjust the plan with the AI until it matches how your app should work. Keep this conversation open — every later prompt builds on the approved plan. If you ever start a new conversation, paste the plan in first.
Approve the Workspace Plan
You should now know what a workspace is in your app, which roles exist, what each role can do and how people join.
The plan says what a workspace represents and what it is called in my app
Every role is listed with what it can do, using my names
I know how people create or join a workspace, and which role they get
I saved a copy of the final plan
Check Your Existing Web App
Confirm Supabase Sign-In Works
Workspaces are added on top of the sign-in your app already has.
People can already sign up and sign in to my app with Supabase Auth
Add User Authentication to Any Website with Supabase
No sign-in yet? Set it up first, then come back.
/video/add-user-authentication-supabase
Inspect Your Project
Nothing changes in this step. The AI maps what exists and plans the smallest safe changes.
Check the Web App
Let AI inspect your sign-in, tables, security rules and existing data, then return a safe plan for adding workspaces before anything changes.
Using the workspace plan we just approved, inspect this project before anything is added. Do not change any files or the database in this task. If the approved plan is not in this conversation, STOP and ask me to paste it. Find and report: - How Supabase Auth is set up: the Supabase client, sign-up, sign-in, sessions and how the app reads the signed-in user - User and profile tables, and how a profile is created for a new account - Every Supabase table the app uses, and which features read or change each one - Existing Row Level Security policies, including any rule that limits records to their owner - Any existing team, company, organization, workspace or membership logic - Any role column, is_admin flag, admin check, custom claim or other permission logic, including anything read from user_metadata - The pages and routes that require a signed-in user, and the main dashboards - Queries that load data without filtering it by user or team - Edge Functions, server routes, API endpoints and database functions that read or change app data - Files stored in Supabase Storage, and how their paths are organized - Whether the app already sends email, for example through a transactional email provider - Whether a Supabase Secret or service-role key appears anywhere in browser code Do NOT assume the SQL in this repository matches what is live in Supabase. Policies and functions are often changed directly in the Supabase Dashboard. Give me ONE read-only SQL query to run in the Supabase SQL Editor. For the app's tables only, it should report: - Whether Row Level Security is enabled - Each policy's name, command, roles, USING and WITH CHECK expressions - Any existing tables, columns or functions related to teams, workspaces, memberships or roles - An approximate row count for each table The query must read metadata and counts only. Do not select or print any user rows. Do not modify anything. I will run the query and paste the result back into this conversation. Then give me a short plan: 1. What already exists 2. What can be reused instead of rebuilt 3. What needs to change 4. Workspace data: each table that should belong to a workspace, and each table that should stay global, with one reason each 5. Safe migration plan: how existing records and existing users will be assigned to workspaces without losing or orphaning anything, and what happens to people who sign up afterwards 6. Blockers: anything I must fix first Rules for the plan: - If a team or workspace system already exists, extend it. Do not create a second one. - Do not rebuild or replace authentication. - Add a workspace only to tables whose records genuinely belong to one workspace. User profiles, app-wide settings and public content usually stay global. - Keep existing rules that limit records to their owner. Workspace rules will work alongside them. Wait for my approval before changing anything.
Run the read-only SQL it gives you in the Supabase SQL Editor, then paste the result back into the same conversation. Approve the plan once it names which tables become workspace data, which stay global, and how existing records will be assigned.
Create the Workspace System
Create the Workspaces Table
The table that holds every workspace in your app.
Create the Workspaces Table
Generate the SQL for the workspaces table, with Row Level Security switched on from the start.
Create the workspaces table from the approved workspace plan, following the implementation plan. If the approved plan or the implementation plan is not in this conversation, STOP and ask me for them. Requirements: - Only the fields this app needs, for example id, name, created_by (the creator's auth.users id) and created_at - If the plan found an existing team, company or workspace table, extend it instead of creating a second one - Name the table workspaces unless the project's existing naming clearly calls for something else, and tell me the name you chose - Enable Row Level Security. Do not add access policies yet — until the security step, nobody can read or change the table from the app, which is the safe default. - Never store workspace access in user_metadata or any other field users can edit - This is the app's own table in this database. Do not use or change Supabase Dashboard Organizations. Give me: 1. A short list of what the SQL creates 2. ONE SQL block to run in the Supabase SQL Editor, written so it is safe to run again 3. What I should see in Supabase afterwards If this project keeps database changes in migration files, also save the same SQL as a new migration so the repository matches what is live. Do not create memberships or change existing tables yet.
Open the SQL Editor in your Supabase project Paste the SQL and run it once Confirm it finishes without errors
Expected result
The Table Editor shows your new workspaces table. It stays empty for now, and the app cannot read it yet.
Add Workspace Memberships
Who belongs to which workspace, and with which role.
Add Workspace Memberships
Generate the memberships table with a role per workspace, duplicate protection and a safe way to create a workspace with its first member.
Now add workspace memberships, following the approved plan. Create a memberships table, for example workspace_members, that links each user (auth.users id) to a workspace, with: - their role in that workspace, limited to the role names from the plan, so the same person can hold a different role in each workspace - when they joined - one membership per person per workspace — the database must refuse duplicates - automatic removal when the workspace or the user account is deleted - indexes on the columns the security rules will look up, such as the user and the workspace Security: - Enable Row Level Security. Add only one policy for now: each signed-in user can read their own memberships. Nothing else yet. - This table is the only source of truth for who belongs to which workspace and with which role. Never copy workspace roles into the access token, user_metadata or any other field users can edit. - Keep workspace roles separate from any app-wide role the project already has, such as a platform admin. Creating a workspace: - The creator must become a member with the creator role from the plan in the same transaction, so a workspace never exists without its first member. - If the plan lets users create workspaces, add a database function the app can call to create one. It takes only what the plan needs from the browser, such as the name — never a user id or a role — and uses the signed-in user from auth.uid(). - If the plan gives every new account a first workspace automatically, create it with a minimal, reliable trigger when a user signs up. A failing trigger would block sign-ups, so keep it simple. - If only I create workspaces, skip the function and give me a short SQL snippet I can run in the SQL Editor to create a workspace for an account by email. Any database function the browser can call must be security definer with a fixed, empty search_path, check that the caller is signed in, and be executable only by signed-in users. Give me: 1. A short list of what the SQL creates 2. ONE SQL block to run in the Supabase SQL Editor, written so it is safe to run again 3. What I should see in Supabase afterwards If this project keeps database changes in migration files, also save the same SQL as a new migration. Do not change existing app tables yet.
Run it in the SQL Editor the same way as the previous SQL.
Expected result
The Table Editor shows the memberships table next to your workspaces table.
Connect Your App Data
Shared data gets a link to its workspace. Global data stays as it is.
Connect Your App Data
Add a workspace link to the tables your plan marked as workspace data, and make the app save the current workspace on every new record.
Now connect the app's own data to workspaces, following the approved plan and the implementation plan. For each table the plan marked as workspace data: - Add a workspace column that references the workspaces table. Keep it optional for now — existing records get their workspace in the next task. - Index it, together with any column the app filters or sorts by in the same queries - Choose what happens to these records if their workspace is deleted, use the safest option that fits the plan, and tell me what you chose - Do not touch tables the plan keeps global, and do not change existing Row Level Security policies yet Then update the app so every NEW record in these tables is saved with the signed-in user's current workspace. Until the workspace screens are added in a later step, the current workspace is the one the user belongs to — the first one they joined if there are several. If they belong to none yet, save the record without one, exactly as today. Keep this in one small helper that the later step will replace. Do not change how existing features look or behave. Give me: 1. A short list of the database and app changes 2. ONE SQL block to run in the Supabase SQL Editor, written so it is safe to run again 3. What I should see afterwards If this project keeps database changes in migration files, also save the same SQL as a new migration. Run the project's build afterwards.
Run the SQL once in the Supabase SQL Editor Let the AI finish the app changes and the build Create one record in your app to make sure it still works
Expected result
Only the tables in your plan have a new workspace column, existing records are untouched, and creating a record in your app still works.
Move Existing Records Into Workspaces
Every existing record gets its workspace, without losing or guessing anything.
Move Existing Records Into Workspaces
Count what needs a workspace, then assign every existing record and user in one safe migration that can run again.
Now give every existing record its workspace, following the migration plan we approved. If the approved migration plan is not in this conversation, STOP and ask me for it. First, give me ONE read-only SQL query that shows, for each workspace table in the plan, how many records exist and how many have no workspace yet, plus how many users have no membership. Counts only — never print row contents. I will run it and paste the result back. Then write the migration: - Follow the plan's approach, for example one workspace for each existing account with that account in the creator role, or one shared workspace with every existing user as a member - Give every existing record exactly one workspace, based on the plan's rule. Never guess: if the rule cannot place a record, list it for me instead of assigning it. - Create the memberships the plan needs, with the roles from the plan - Never delete or overwrite existing data, and change nothing outside the tables in the plan - Run it as one transaction, so a failure changes nothing - Make it safe to run again without creating duplicate workspaces or memberships Make the workspace column required only when every existing record has a workspace AND the app already saves a workspace on every new record. If either is not true yet, leave it optional and tell me why. Give me: 1. What the migration will do, with the expected number of new workspaces, new memberships and updated records, based on my counts 2. ONE SQL block to run in the Supabase SQL Editor 3. The same read-only count query to run afterwards 4. What I should see afterwards If this project keeps database changes in migration files, also save the same SQL as a new migration.
Back up before you move live data
This migration updates existing records. If people already use your app, back up your database first and run it at a quiet time.
Run the count query and paste the result back into the conversation Check the AI's summary and expected numbers against your counts Run the migration once in the SQL Editor Run the count query again and paste the result back
Expected result
The count query shows no records without a workspace, and every existing user belongs to the workspace your plan gave them.
Verify the Relationships
A read-only check that the structure is complete before any security rules depend on it.
Verify the Workspace Structure
Get one read-only query that shows every workspace has a member in charge, every record has a workspace and nothing is duplicated.
Give me ONE read-only SQL query that verifies the workspace structure before any security rules depend on it. It should report: - Each workspace with its number of members, and whether it has at least one member with full control, as defined in the plan - Any person who is a member of the same workspace more than once (expected: none) - For each workspace table in the plan, the number of records without a workspace (expected: 0, unless you listed records the plan could not place) - Whether each workspace table references the workspaces table and has an index on its workspace column Show workspace ids and counts only — no record contents and no email addresses. Do not change anything. When I paste the result back, tell me whether everything is correct, and fix anything that is not before we continue.
Run it in the SQL Editor and paste the result back into the conversation.
Expected result
Every workspace has a member with full control, every workspace record belongs to a workspace, and nobody is a member of the same workspace twice.
Protect Every Workspace With RLS
UI filtering vs. RLS
Workspace filtering in the UI improves the experience. RLS is what actually protects the data. This step makes Supabase refuse every request for another workspace's data — even when someone changes a URL, edits a workspace ID or calls the API directly.
Protect the Workspace Tables
Membership checks, plus safe rules for workspaces and their members.
Protect the Workspace Tables
Add reusable membership checks and lock down the workspaces and memberships tables, including role changes and the last member in charge.
Now protect the workspace system itself — the workspaces and memberships tables — following the approved plan. If the approved plan is not in this conversation, STOP and ask me for it. 1. Membership checks. Create small helper functions that answer, for the signed-in user from auth.uid(): does this user belong to this workspace, and does their role there allow this action? Read the role from the memberships table every time — never from the access token, user_metadata or anything the browser sends. Make them security definer with a fixed, empty search_path, so they do not trigger the memberships table's own policies again (a policy that queries its own table causes infinite recursion). Keep them in a schema that is not exposed through the API, and grant signed-in users only what the policies need. 2. Workspaces table: - Members can read the workspaces they belong to. Nobody else can. - Renaming and deleting follow the plan's roles. - New workspaces are created only through the create function from the earlier step, if the plan lets users create them. 3. Memberships table: - Members can see who else belongs to their own workspaces, and nothing about other workspaces. - Nobody can add, change or delete a membership directly from the browser. Joining happens only through invitations, in a later step. - Changing a role, removing a member and leaving a workspace go through database functions that check the caller's role against the plan. - Nobody can raise their own role or grant a role the plan does not let them grant. - A workspace can never lose its last member with full control, as defined in the plan: refuse any removal, role change or leave that would leave none. Every database function the browser can call must be security definer with a fixed, empty search_path, identify the caller with auth.uid(), check any workspace or user sent by the browser against the caller's own membership, and be executable only by signed-in users. Keep any app-wide role, such as a platform admin, out of these checks unless the plan says otherwise. Follow current Supabase guidance for policy performance, for example wrapping auth.uid() in a select. Give me: 1. A plain-language list of every policy and function you will add or change, and why 2. ONE SQL block to run in the Supabase SQL Editor, written so it is safe to run again 3. ONE read-only query I can run afterwards to confirm the live policies on both tables 4. What I should see afterwards If this project keeps database changes in migration files, also save the same SQL as a new migration. Do not change the app's own data tables or app code yet.
Run the SQL once in the Supabase SQL Editor Run the read-only check query and paste the result back Continue once the AI confirms both tables match your plan
Expected result
People can see only their own workspaces and teammates, and every membership change goes through a checked database function.
Protect Your Workspace Data
Every workspace table requires membership, and every action requires the right role.
Protect Your Workspace Data
Update the Row Level Security policies on every workspace table so only members reach its data, with only the actions their role allows.
Now protect the app's own workspace data — every table the plan marked as workspace data — using the membership checks from the previous task. If the approved plan, the implementation plan or the live policy report from the inspection is not in this conversation, STOP and ask me for them. Do not guess the current policies. For each workspace table, allow only the actions the plan's roles allow: - Read: only members of the record's workspace - Create: only members whose role may create it, and only in a workspace they belong to. Check the workspace on the new record — never trust it just because the app sent it. - Edit: only members whose role may edit it. A record can never be moved to another workspace. - Delete: only members whose role may delete it Cover only the actions this app actually uses on each table. Keep existing protection. Where the app already limits records to their owner — for example, a member may edit only their own tasks — combine the workspace check with that ownership rule using auth.uid(). Do not remove ownership rules that still apply. Replace, do not just add. Supabase policies combine permissively: a new workspace policy next to an old policy that allows every signed-in user changes nothing. Replace or remove only the in-scope policies that allow more than the plan. Rules: - Never use true for workspace data - Do not disable Row Level Security to make something work - Leave global tables, intentionally public tables and their policies unchanged - Signed-out users must not reach workspace data - If workspace files are stored in Supabase Storage, protect them with the same membership checks - Follow current Supabase guidance for policy performance Give me: 1. A plain-language list of every policy you will add, change or remove, per table, and why 2. ONE SQL block to run in the Supabase SQL Editor 3. ONE read-only query I can run afterwards to confirm the live policies 4. What I should see afterwards If this project keeps database changes in migration files, also save the same SQL as a new migration. Do not change app code yet.
Review the policy changes before you run them
This SQL can replace existing access rules. Read the AI's list of changes first — a wrong policy can expose one workspace's data to another or lock your real users out.
Run the SQL once in the Supabase SQL Editor Run the read-only check query and paste the result back Continue once the AI confirms every workspace table matches your plan
Expected result
Every workspace table requires membership, each action requires the right role, and no remaining policy allows more than your plan.
Protect Server-Side Actions
Server code that uses the Secret key skips RLS, so it must check membership itself. If the plan found none, this prompt simply confirms that.
Protect Server-Side Actions
Make every Edge Function, server route and database function check the caller's workspace membership and role on the server.
Now protect the server-side code from the implementation plan: Edge Functions, server routes, API endpoints and database functions that read or change workspace data. For each one: - Verify the caller's session on the server before doing anything, and reject requests without a valid session - Check on the server that the caller belongs to the workspace they are acting on and that their role allows the action. Never trust a workspace id, role or user id from the request body, query string or headers without that check. - Code that uses the Supabase Secret or service-role key bypasses Row Level Security, so it must make these checks itself, and the key must stay on the server only - A database function marked security definer also bypasses Row Level Security, so it must check membership and role itself - Return a clear permission error when access is refused If there are no such actions, say so and change nothing. When you are done, list each action, the workspace check it now requires, and exactly where the check happens. Run the project's build afterwards.
Expected result
A signed-in user can reach records only from workspaces they belong to — through the app, the Supabase API or your server code — and only with the actions their role allows.
Add Secure Member Invitations
Create the Invitations Table
Where invitations are stored — with a hash of each token, never the token itself.
Create the Invitations Table
Generate the SQL for workspace invitations: one workspace, one email, one role, an expiry and a hashed token.
Now add member invitations, following the approved plan. Start with where invitations are stored. If the approved plan is not in this conversation, STOP and ask me for it. Create an invitations table, for example workspace_invitations, with only what this app needs: - the workspace it belongs to - the invited email address, stored in lowercase - the role the invitation grants, limited to the plan's roles - who sent it - a hash of the invitation token — never the token itself - when it expires - when it was accepted, and by whom - when it was revoked - when it was created Requirements: - Enable Row Level Security. Members whose role may invite can read their own workspace's invitations. Nobody can create, change or delete invitations directly from the browser — the next tasks add checked database functions for that. - At most one pending invitation per email address per workspace - Remove a workspace's invitations when the workspace is deleted - Index what the functions will look up, such as the token hash Give me: 1. A short list of what the SQL creates 2. ONE SQL block to run in the Supabase SQL Editor, written so it is safe to run again 3. What I should see in Supabase afterwards If this project keeps database changes in migration files, also save the same SQL as a new migration. Do not add the functions or change app code yet.
Run it in the SQL Editor the same way as the earlier SQL.
Expected result
The Table Editor shows the new invitations table, still empty.
Create Secure Invite Links
Members who may invite get a link to copy and share — no email setup needed.
Create Secure Invite Links
Add the database functions that create and revoke invitations, and an invite form that shows a link to copy.
Now let members create secure invite links. 1. Add a database function that creates an invitation. It must: - check that the signed-in user belongs to the workspace and that their role may invite members, according to the plan - accept a role only if the plan lets this user grant it, and use the plan's default role for invited members when none is chosen. The browser can never pick an arbitrary role. - generate a long random token with a cryptographically secure generator, store only its hash, and return the token once so the app can build the link - set an expiry, for example 7 days - refuse to invite someone who is already a member, and replace an older pending invitation for the same email in the same workspace - be security definer with a fixed, empty search_path, and executable only by signed-in users 2. Add a function to revoke a pending invitation, with the same permission check. 3. In the app, add a simple invite form for members who may invite: an email address, and a role they are allowed to grant, preset to the plan's default role. After the invitation is created, show the full invite link with a Copy button and when it expires. Other members do not see the form — the database refuses them anyway. The link opens a page in this app, for example /invite?token=..., which the next task builds. Invite links must work without any email setup. If this project already sends email, you may also send the link through that existing setup, but copying the link must always work. Never put the Supabase Secret or service-role key in browser code. Give me ONE SQL block to run in the Supabase SQL Editor (also saved as a migration if the project uses them), then make the app changes and run the project's build.
Run the SQL in the Supabase SQL Editor and let the AI finish the app changes Sign in as a member who may invite Invite an email address you can sign in with, and copy the link
Expected result
The invitations table shows one pending invitation with an expiry date. It stores only a hash — the token from your link appears nowhere in the table.
Accept Invites and Join the Workspace
The link works for people who already have an account and for people who still need to sign up.
Accept Invites Safely
Add the database function and the invite page that accept an invitation once, for the invited email only, with the invited role.
Now build the accept side of invitations. 1. Add a database function that accepts an invitation by its token. In one transaction it must: - require a signed-in user - find the invitation by the token's hash, and refuse it if it is expired, revoked or already used - require the signed-in user's email address to match the invited email and to be confirmed - create the membership with exactly the role stored on the invitation — nothing sent by the browser - mark the invitation as used by this user, so the same link can never be used twice, even if two requests arrive at the same time - if the user is already a member, keep their current role and tell them so - be security definer with a fixed, empty search_path, and executable only by signed-in users 2. In the app, add the invite page the links open: - Signed in with the invited email: accept the invitation, then open the workspace - Not signed in, or no account yet: show clear Sign in and Create account options, keep the invitation through sign-up and email confirmation, and finish accepting once the person is signed in. Reuse the app's existing sign-in and sign-up — do not rebuild authentication. - Signed in with a different email: explain that this invitation is for another email address, and offer to sign out - Expired, revoked or already used: a clear message, and no membership If the sign-up or email-confirmation redirect needs a new URL, tell me exactly what to add under Authentication → URL Configuration in Supabase. If email confirmation is turned off in this Supabase project, tell me, because matching the invited email is much weaker without it. Give me ONE SQL block to run in the Supabase SQL Editor (also saved as a migration if the project uses them), then make the app changes and run the project's build.
Run the SQL in the Supabase SQL Editor and let the AI finish the app changes Open your invite link in a private browser window Sign in or create an account with the invited email address
Expected result
The account joins the workspace with the invited role, the memberships table shows it, and the invitation is marked as used.
Verify Expiry and Reuse Protection
A used, expired or misdirected invitation must never create a membership.
Open the invite link you already used Create a new invitation, ask the AI for a SQL snippet that moves its expiry into the past, run it in the SQL Editor, then open the link Create one more invitation and open it while signed in with a different account
The used link is refused and creates no second membership
The expired link is refused
A different account cannot accept someone else's invitation
Add the Workspace Experience
Connect Workspaces to Your App
The interface follows the rules Supabase now enforces. It never replaces them.
Connect Workspaces to Your App
Add the current workspace, switching, workspace creation and role-aware screens in your app's existing design — only what your plan needs.
Now connect the workspace system to the app's interface, following the approved plan and the implementation plan. If the approved plan is not in this conversation, STOP and ask me for it. Inspect the current layout, navigation and components first. Add only what the plan needs, in the app's existing design. Where the current workspace and the switcher should appear: {{SWITCHER_PLACEMENT}} Anything else to include or leave out: {{EXTRA_REQUESTS}} Build: - Current workspace: load the signed-in user's memberships and keep one active workspace. Show its name where people will notice it. - Switcher: only if people can belong to more than one workspace. Switching clears the previous workspace's data and reloads every workspace query, so nothing from the old workspace shows even for a moment. - Remember the last active workspace, for example in localStorage, but treat it only as a preference. Check it against the user's current memberships every time, and fall back to another workspace they belong to when it is no longer valid. It is never a permission. - Create workspace: only if the plan lets users create their own, through the create function from the database step - Workspace data: every query for workspace data filters by the active workspace, and every new record is saved to it. Replace the temporary current-workspace helper from the earlier step. - Role-aware interface: show or hide actions from the user's role in the active workspace, through one shared helper rather than scattered role-name checks. Hidden buttons are only user experience — the database already refuses forbidden actions. - If Supabase refuses a request, show a clear message instead of a broken screen Handle these cases: - No workspace yet: a simple screen to create one or wait for an invitation, depending on the plan - One workspace: open it directly, with no unnecessary switcher - Several workspaces: open the remembered one, or let the person choose - Removed from the active workspace, or the active workspace was deleted: move to another workspace they belong to, or to the no-workspace screen, without showing stale data - Role changed: load the current role whenever the active workspace loads, and again when a request is refused Do not: - Rebuild authentication - Treat localStorage, the URL or a selected workspace id as permission - Change database policies to make the interface work When you are done, list the files you changed and how each case behaves. Run the project's build afterwards.
Where should people see and switch their workspace?
Anything the workspace screens should include or leave out?
Show the workspace name in the page header. Leave out workspace settings for now.
Expected result
You can open or create a workspace, see which one is active, and switch without seeing the previous workspace's data. New records land in the active workspace.
Add Member Management
Invite, change roles, remove and leave — each shown only to the roles your plan allows.
Add Member Management
Add a members page that reuses your invite form and the checked database functions, and handles the last member in charge clearly.
Now add member management for the active workspace, following the approved plan. Build only what the plan needs, in the app's existing design: - Members: list each member with their name or email and their role. Take names from the existing profiles table if there is one; otherwise add a database function that returns them only to members of the same workspace. Never expose auth.users directly. - Invitations: reuse the invite form from the invitations step, and list pending invitations with their expiry and a Revoke option for members who may invite - Change role and remove member: only for the roles the plan allows, through the database functions from the security step - Leave workspace: available to members, except that the last member with full control must hand that role to someone else first. Explain why instead of failing silently. - Workspace settings, such as rename or delete: only if the plan includes them, and only for the roles it names. Deleting asks for confirmation and says exactly what will be removed. Every one of these actions is enforced by the database. The interface only shows each action to the roles that can use it and explains any refusal clearly. When someone leaves or is removed, or the workspace is deleted, move them out of it right away without showing stale data. Do not build an app-wide admin area, and do not rebuild the invitation flow. When you are done, list the files you changed and what each role can do on the members page. Run the project's build afterwards.
Expected result
Members who may manage the team can invite, change roles and remove people. Everyone else sees only what their role allows, and the last member with full control cannot leave.
Test Workspace Security & Collaboration
Create Your Test Setup
Two test accounts and two workspaces — enough to prove who can and cannot reach what.
Sign up two test accounts in your app: User A and User B As User A, create Workspace A and add a test record As User B, create Workspace B and add a test record As User A, invite User B to Workspace A with your most restricted role, then accept the invitation as User B
If people cannot create workspaces in your app, ask the AI for a SQL snippet that creates one for a test account.
Expected result
You should now see both test users in your memberships table with different roles: User B created Workspace B and has your most restricted role in Workspace A.
Verify the Database Boundary
A hidden button is not proof. This checks that Supabase itself refuses everything outside each person's workspaces and roles.
Verify Workspace Isolation in Supabase
Test both accounts directly against Supabase so cross-workspace access, fake workspace IDs, forbidden roles and bad invitations are proven to be refused.
Test the workspace rules directly at the Supabase level, not through the interface, using my test setup: - Workspace A: User A has the creator role, and User B has the most restricted role - Workspace B: User B has the creator role, and User A is not a member Use the safest method already available in this project: 1. An existing test runner or test utilities, if the project has them 2. Otherwise a temporary LOCAL-ONLY script that uses the project's normal Supabase client and its Publishable or public key Do NOT use the Secret or service-role key. It bypasses Row Level Security, so the test would prove nothing. Ask me to put the test accounts' credentials in local, uncommitted environment variables. Never commit passwords, never put them in source code and never print them. For each workspace table in the plan: - ISOLATION: as User A, try to read, create, edit and delete records in Workspace B. Expected: refused — an error, no rows returned or zero rows changed. Re-read afterwards to confirm nothing changed. - FAKE WORKSPACE ID: as User A, create a record carrying Workspace B's id, and try to move a Workspace A record into Workspace B. Expected: refused. - RESTRICTED ROLE: as User B in Workspace A, try each action the plan does not allow that role. Expected: refused. - ALLOWED ACTIONS: as User A in Workspace A, and as User B in Workspace B, perform one allowed action of each kind. Expected: it works. For the workspace system: - MEMBERSHIPS: as User B, try to raise their own role in Workspace A and to add themselves to a workspace directly. As User A, try to read Workspace B's members. Expected: refused. - INVITATIONS: as User A, create an invitation to Workspace A. Expected: it works. As User B, try the same if their role may not invite. Expected: refused. Try to accept an already-used invitation again. Expected: refused. For expiry, give me a SQL snippet that expires one test invitation in the SQL Editor, then try to accept it. Expected: refused. - SIGNED OUT: repeat one read and one write with no session. Expected: nothing returned and nothing changed. - SERVER ACTIONS: call each protected Edge Function, server route or database function for a workspace the account does not belong to. Expected: refused. Use test data only. Do not change production data. Keep any temporary script until I finish the last test in this step, and never commit it. Report expected versus actual for every test, then finish with exactly one verdict: WORKSPACES ISOLATED or NEEDS ATTENTION If NEEDS ATTENTION, list only what is still failing.
Continue when the AI reports WORKSPACES ISOLATED. If it reports NEEDS ATTENTION, fix the listed issues and run the check again.
Test Each User in the App
A private browser window makes switching between the two accounts easy.
User A sees Workspace A and its record, and never Workspace B
User B sees both workspaces, and switching shows only the selected workspace's records
As User A, opening a Workspace B link or pasting its ID into the address bar shows no Workspace B data
In Workspace A, User B cannot use the actions their restricted role does not allow
Features that have nothing to do with workspaces still work
Test a Role Change and a Removal
When membership changes, access follows right away.
As User A, give User B a role with more access in Workspace A As User B, reload the app and use an action the new role allows As User A, remove User B from Workspace A, then try to leave Workspace A yourself As User B, reload the app and try to open Workspace A Ask the AI to rerun its database check as User B for Workspace A, then remove any temporary test script
User B's new role works right away
User A cannot leave while they are the only member with full control
After removal, User B cannot open Workspace A or see its records
The database check returns nothing from Workspace A for User B
Your web app now supports real teams and workspaces, with secure member access and isolated data for every workspace.
Optional upgrades, whenever you want them:
Add Transactional Emails to Any Website or Web App
Send invitations by email automatically instead of copying links.
/video/add-transactional-emails-to-any-website-or-web-app
Add Roles & Permissions to Any Web App With Supabase
Add app-wide roles, such as a platform admin, alongside your workspace roles.
/video/add-roles-and-permissions-supabase
Add an Admin Dashboard to Any AI-Built Web App
Manage workspaces and users from one platform-level admin area.
/video/add-an-admin-dashboard-to-any-ai-built-web-app