diff --git a/apps/design-system/app/layout.tsx b/apps/design-system/app/layout.tsx index e6c4f02ef17..a7d6c82e2dd 100644 --- a/apps/design-system/app/layout.tsx +++ b/apps/design-system/app/layout.tsx @@ -1,5 +1,5 @@ import '@/styles/globals.css' -import '../../studio/styles/typography.scss' +import '../../studio/styles/typography.css' import type { Metadata, Viewport } from 'next' diff --git a/apps/design-system/postcss.config.cjs b/apps/design-system/postcss.config.cjs index 08a01d4d167..29c447cb54b 100644 --- a/apps/design-system/postcss.config.cjs +++ b/apps/design-system/postcss.config.cjs @@ -1,5 +1 @@ -module.exports = { - plugins: { - tailwindcss: {}, - }, -} +module.exports = require('config/postcss.config') diff --git a/apps/docs/app/layout.tsx b/apps/docs/app/layout.tsx index fbde324a0a4..e470d95e69b 100644 --- a/apps/docs/app/layout.tsx +++ b/apps/docs/app/layout.tsx @@ -1,9 +1,9 @@ import '@code-hike/mdx/styles.css' -import 'config/code-hike.scss' +import 'config/code-hike.css' import 'ui-patterns/ShimmeringLoader/index.css' -import '../styles/main.scss' -import '../styles/new-docs.scss' -import '../styles/prism-okaidia.scss' +import '../styles/main.css' +import '../styles/new-docs.css' +import '../styles/prism-okaidia.css' import { GlobalProviders } from '~/features/app.providers' import { TopNavSkeleton } from '~/layouts/MainSkeleton' diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts index 8d3b08dbd47..3e9a854c652 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts @@ -1040,6 +1040,10 @@ export const database: NavMenuConstant = { name: 'Implementing cascade deletes', url: '/guides/database/postgres/cascade-deletes' as `/${string}`, }, + { + name: 'Deleting data and dropping objects safely', + url: '/guides/database/postgres/data-deletion' as `/${string}`, + }, { name: 'Managing enums', url: '/guides/database/postgres/enums' }, { name: 'Managing database functions', @@ -1490,7 +1494,7 @@ export const api: NavMenuConstant = { items: [ { name: 'How API Keys work', url: '/guides/api/api-keys' }, { name: 'Securing your API', url: '/guides/api/securing-your-api' }, - { name: 'Hardening the Data API', url: '/guides/api/hardening-data-api' }, + { name: 'Data API', url: '/guides/database/data-api' }, { name: 'Custom Claims & RBAC', url: '/guides/api/custom-claims-and-role-based-access-control-rbac', @@ -2481,7 +2485,7 @@ export const security: NavMenuConstant = { url: '/guides/deployment/shared-responsibility-model' as `/${string}`, }, { name: 'Row Level Security', url: '/guides/database/postgres/row-level-security' }, - { name: 'Hardening the Data API', url: '/guides/api/hardening-data-api' }, + { name: 'Data API', url: '/guides/database/data-api' }, ], }, ], @@ -2584,12 +2588,28 @@ export const platform: NavMenuConstant = { enabled: fullPlatformEnabled, items: [ { name: 'Overview', url: '/guides/platform/sso' as `/${string}` }, + { + name: 'Understanding Login Flows', + url: '/guides/platform/sso/login-flows' as `/${string}`, + }, + { + name: 'Choosing a Login Flow', + url: '/guides/platform/sso/choosing-login-flow' as `/${string}`, + }, { name: 'SSO with Azure AD', url: '/guides/platform/sso/azure' }, { name: 'SSO with Google Workspace', url: '/guides/platform/sso/gsuite' as `/${string}`, }, { name: 'SSO with Okta', url: '/guides/platform/sso/okta' }, + { + name: 'Multiple SSO Providers', + url: '/guides/platform/sso/multiple-providers' as `/${string}`, + }, + { + name: 'Testing and Best Practices', + url: '/guides/platform/sso/testing-best-practices' as `/${string}`, + }, ], }, ], diff --git a/apps/docs/content/_partials/quickstart_db_setup.mdx b/apps/docs/content/_partials/quickstart_db_setup.mdx index 7c9489d64bb..ab4f09b4da3 100644 --- a/apps/docs/content/_partials/quickstart_db_setup.mdx +++ b/apps/docs/content/_partials/quickstart_db_setup.mdx @@ -32,7 +32,7 @@ curl -X POST https://api.supabase.com/v1/projects \ -When your project is up and running, go to the [Table Editor](/dashboard/project/_/editor), create a new table and insert some data. +When your project is up and running, go to the [**Table Editor**](/dashboard/project/_/editor) section of the Dashboard, create a new table and insert some data. Then in the [**Integrations > Data API**](/dashboard/project/_/integrations/data_api/settings) section of the Dashboard, expose the specific tables or functions you want to access. To automatically grant access for new tables and functions in `public`, enable **Default privileges for new entities**. Alternatively, you can run the following snippet in your project's [SQL Editor](/dashboard/project/_/sql/new). This will create an `instruments` table with some sample data. @@ -54,6 +54,9 @@ Alternatively, you can run the following snippet in your project's [SQL Editor]( ('cello'); alter table instruments enable row level security; + + -- Enable read access for the Data API + grant select on public.instruments to anon; ``` diff --git a/apps/docs/content/guides/api/automatic-retries-in-supabase-js.mdx b/apps/docs/content/guides/api/automatic-retries-in-supabase-js.mdx index 16fa4ce1f8c..a842d73e1c6 100644 --- a/apps/docs/content/guides/api/automatic-retries-in-supabase-js.mdx +++ b/apps/docs/content/guides/api/automatic-retries-in-supabase-js.mdx @@ -108,7 +108,7 @@ const fetchWithRetry = fetchRetry(fetch, { attempt < 3 && response && response.status == 520 // Cloudflare errors - && response.url.includes('rpc/your_stored_procedure') + && response.url.includes('rpc/your_database_function') if (shouldRetry(attempt, error, response)) { console.log(`Retrying request... Attempt #${attempt}`, response) @@ -119,9 +119,9 @@ const fetchWithRetry = fetchRetry(fetch, { } }) -async function yourStoredProcedure() { +async function yourDatabaseFunction() { const { data, error } = await supabase - .rpc('your_stored_procedure', { param1: 'value1' }); + .rpc('your_database_function', { param1: 'value1' }); if (error) { console.log('Error executing RPC:', error); @@ -130,10 +130,10 @@ async function yourStoredProcedure() { } } -yourStoredProcedure(); +yourDatabaseFunction(); ``` -By using `retryOn` with a custom function, you can define specific conditions for retrying requests. In this example, the retry logic is applied only to requests targeting a specific stored procedure. +By using `retryOn` with a custom function, you can define specific conditions for retrying requests. In this example, the retry logic is applied only to requests targeting a specific database function. ## Conclusion diff --git a/apps/docs/content/guides/api/creating-routes.mdx b/apps/docs/content/guides/api/creating-routes.mdx index 7c84e6cc82e..7d52ed85137 100644 --- a/apps/docs/content/guides/api/creating-routes.mdx +++ b/apps/docs/content/guides/api/creating-routes.mdx @@ -25,29 +25,39 @@ This creates a corresponding route `todos` which can accept `GET`, `POST`, `PATC 1. Click **Save**. 1. Click **New Column** and create a column with the name `task` and type `text`. 1. Click **Save**. - - +1. In the [**Integrations > Data API**](/dashboard/project/_/integrations/data_api/settings) section of the Dashboard, expose specific tables like `todos` or the functions you want to access. To automatically grant access for new tables and functions in `public`, enable **Default privileges for new entities**. ```sql - -- Create a table called "todos" with a column to store tasks. +-- Create a table called "todos" with a column to store tasks. create table todos ( id bigint generated by default as identity primary key, task text check (char_length(task) > 3) ); + +-- Enable Data API access with least-privilege grants +-- Allow read-only access for anonymous clients +grant select on public.todos to anon; +-- Allow full CRUD for authenticated clients +grant select, insert, update, delete on public.todos to authenticated; +-- Allow full CRUD for the server-side service role +grant select, insert, update, delete on public.todos to service_role; +-- Important: enable Row Level Security and create appropriate policies +-- before granting write access to client roles (see RLS guide) ``` + + +Granting privileges (like `select` or `execute`) to roles such as `anon` or `authenticated` makes those tables or functions accessible through the Data API. Behind the scenes, the API checks your Postgres permissions—only objects with explicit grants are exposed, and all other access is denied by default. + + + ## API URL and keys Every Supabase project has a unique API URL. Your API is secured behind an API gateway which requires an API Key for every request. @@ -92,6 +102,7 @@ using the API URL (`SUPABASE_URL`) and Key (`SUPABASE_PUBLISHABLE_KEY`) we provi ```javascript // Initialize the JS client import { createClient } from '@supabase/supabase-js' + const supabase = createClient(SUPABASE_URL, SUPABASE_PUBLISHABLE_KEY) // Make a request diff --git a/apps/docs/content/guides/api/hardening-data-api.mdx b/apps/docs/content/guides/api/hardening-data-api.mdx deleted file mode 100644 index 7c6e017d59f..00000000000 --- a/apps/docs/content/guides/api/hardening-data-api.mdx +++ /dev/null @@ -1,137 +0,0 @@ ---- -title: 'Hardening the Data API' ---- - -Your database's auto-generated Data API exposes the `public` schema by default. You can change this to any schema in your database, or even disable the Data API completely. - -Any tables that are accessible through the Data API _must_ have [Row Level Security](/docs/guides/database/postgres/row-level-security) enabled. Row Level Security (RLS) is enabled by default when you create tables from the Supabase Dashboard. If you create a table using the SQL editor or your own SQL client or migration runner, you*must* enable RLS yourself. - -## Shared responsibility - -Your application's security is your responsibility as a developer. This includes RLS, falling under the [Shared Responsibility](/docs/guides/deployment/shared-responsibility-model) model. To help you: - -- Supabase sends daily emails warning of any tables that are exposed to the Data API which do not have RLS enabled. -- Supabase provides a Security Advisor and other tools in the Supabase Dashboard to fix any issues. - -## Private schemas - -We highly recommend creating a `private` schema for storing tables that you do not want to expose via the Data API. These tables can be accessed via Supabase Edge Functions or any other serverside tool. In this model, you should implement your security model in your serverside code. Although it's not required, we _still_ recommend enabling RLS for private tables and then connecting to your database using a Postgres role with `bypassrls` privileges. - -## Managing the public schema - -If your `public` schema is used by other tools as a default space, you might want to lock down this schema. This helps prevent accidental exposure of data that's automatically added to `public`. - -There are several levels of security hardening for the Data API: - -- [Disabling the Data API entirely](#disabling-the-data-api). This is recommended if you _never_ need to access your database via Supabase client libraries or the REST and GraphQL endpoints. -- [Exposing a custom schema](#exposing-a-custom-schema-instead-of-public) instead of `public`, giving you explicit control over what is accessible. -- [Automatically enabling RLS on new tables](#automatically-enabling-rls-on-new-tables) using an event trigger. -- [Adjusting table-level grants](#table-level-grants) to control which roles can access specific tables. - -## Disabling the Data API - -You can disable the Data API entirely if you never intend to use the Supabase client libraries or the REST and GraphQL data endpoints. For example, if you only access your database via a direct connection on the server, disabling the Data API gives you the greatest layer of protection. - -1. Go to [API Settings](/dashboard/project/_/settings/api) in the Supabase Dashboard. -1. Under **Data API Settings**, toggle **Enable Data API** off. - -## Exposing a custom schema instead of `public` - -If you want to use the Data API but with increased security, you can expose a custom schema instead of `public`. By not using `public`, which is often used as a default space and has laxer default permissions, you get more conscious control over your exposed data. - -Any data, views, or functions that should be exposed need to be deliberately put within your custom schema (which we will call `api`), rather than ending up there by default. - -### Step 1: Remove `public` from exposed schemas - -1. Go to [**API Settings**](/dashboard/project/_/settings/api) in the Supabase Dashboard. -1. Under **Data API Settings**, remove `public` from **Exposed schemas**. Also remove `public` from **Extra search path**. -1. Click **Save**. -1. Go to [**Database Extensions**](/dashboard/project/_/database/extensions) and disable the `pg_graphql` extension. - -### Step 2: Create an `api` schema and expose it - -1. Connect to your database. You can use `psql`, the [Supabase SQL Editor](/dashboard/project/_/sql), or the Postgres client of your choice. - -1. Create a new schema named `api`: - - ```sql - create schema if not exists api; - ``` - -1. Grant the `anon` and `authenticated` roles usage on this schema. - - ```sql - grant usage on schema api to anon, authenticated; - ``` - -1. Go to [API Settings](/dashboard/project/_/settings/api) in the Supabase Dashboard. - -1. Under **Data API Settings**, add `api` to **Exposed schemas**. Make sure it is the first schema in the list, so that it will be searched first by default. - -1. Under these new settings, `anon` and `authenticated` can execute functions defined in the `api` schema, but they have no automatic permissions on any tables. On a table-by-table basis, you can grant them permissions. For example: - - ```sql - grant select on table api. to anon; - grant select, insert, update, delete on table api. to authenticated; - ``` - -## Automatically enabling RLS on new tables - -Tables created via the Supabase Dashboard have RLS enabled by default. However, if you or your team create tables using the SQL editor, migrations, or an external tool, RLS will not be enabled automatically. - -You can use an [event trigger](/docs/guides/database/postgres/event-triggers#example-trigger-function---auto-enable-row-level-security) to automatically enable RLS whenever a new table is created in the `public` schema. This ensures that no table is accidentally left exposed without RLS protection. - -## Table-level grants - -By default, tables in the `public` schema are granted full access (`SELECT`, `INSERT`, `UPDATE`, `DELETE`) to the `anon` and `authenticated` roles. This allows the Data API to query those tables on behalf of users. - -You can adjust these privileges on a per-table basis to restrict which operations each role can perform. For example, you might want to: - -- Allow `anon` users to only `SELECT` from a table, preventing anonymous writes. -- Prevent `anon` users from accessing a table entirely, making it available only to authenticated users. -- Restrict `authenticated` users to `SELECT` and `INSERT` only, preventing updates and deletes. - - - -Table-level privileges work alongside [Row Level Security](/docs/guides/database/postgres/row-level-security). Privileges control _which operations_ are possible, while RLS policies control _which rows_ are accessible. For full protection, use both: restrict privileges to limit operation types, and use RLS policies to control row-level access. - - - -### Adjusting table-level grants via the Dashboard - - - -Adjusting table-level privileges via the Dashboard is currently in beta and will be available via gradual roll-out. - - - -1. Go to [**Table Editor**](/dashboard/project/_/editor) in the Supabase Dashboard. -2. Select the table you want to configure. -3. Click the vertical dots icon to open the table menu and select "Edit table". -4. Under **Data API Access**, click the settings icon to open **Adjust API privileges per role**. -5. For each role (`anon` and `authenticated`), select or deselect the privileges you want to grant. -6. Click **Save**. - -### Adjusting table-level grants via SQL - -You can also adjust privileges using SQL. For example, to allow only `SELECT` access for `anon` on a table: - -```sql --- Revoke all existing privileges -revoke all on table public.your_table from anon; - --- Grant only SELECT -grant select on table public.your_table to anon; -``` - -To remove all access for `anon` from a table: - -```sql -revoke all on table public.your_table from anon; -``` - -To restore full access: - -```sql -grant select, insert, update, delete on table public.your_table to anon; -``` diff --git a/apps/docs/content/guides/api/quickstart.mdx b/apps/docs/content/guides/api/quickstart.mdx index 81558335907..08a0bc9e17e 100644 --- a/apps/docs/content/guides/api/quickstart.mdx +++ b/apps/docs/content/guides/api/quickstart.mdx @@ -38,17 +38,21 @@ We'll create a database table called `todos` for storing tasks. This creates a c id serial primary key, task text ); + + -- Enable Data API access + -- Allow read-only access for anonymous clients + grant select on public.todos to anon; ``` - + 1. Go to the [**Table editor**](/dashboard/project/_/editor) section in the Dashboard. + 2. Click **New Table** and create a table with the name `todos`. + 3. Click **Save**. + 4. Click **New Column** and create a column with the name `task` and type `text`. + 5. Click **Save**. + 6. In the [**Integrations > Data API**](/dashboard/project/_/integrations/data_api/settings) section of the Dashboard, expose the `todos` table. To automatically grant access for new tables and functions in `public`, enable **Default privileges for new entities**. @@ -61,7 +65,7 @@ We'll create a database table called `todos` for storing tasks. This creates a c - Let's turn on Row Level Security for this table and allow public access. + Let's turn on Row Level Security for this table, create the required policies, and then grant any write access. @@ -78,6 +82,18 @@ We'll create a database table called `todos` for storing tasks. This creates a c for select to anon using (true); + + -- Allow authenticated users to read and modify todos + create policy "Allow authenticated users to manage todos" + on todos + for all + to authenticated + using (true) + with check (true); + + -- Grant write access only after RLS and policies are in place + grant select, insert, update, delete on public.todos to authenticated; + grant select, insert, update, delete on public.todos to service_role; ``` diff --git a/apps/docs/content/guides/api/securing-your-api.mdx b/apps/docs/content/guides/api/securing-your-api.mdx index 416476cf744..59d667a4a6e 100644 --- a/apps/docs/content/guides/api/securing-your-api.mdx +++ b/apps/docs/content/guides/api/securing-your-api.mdx @@ -8,15 +8,15 @@ The data APIs are designed to work with Postgres Row Level Security (RLS). If yo To control access to your data, you can use [Policies](/docs/guides/auth#policies). -## Enabling row level security +## Add RLS policies -Any table you create in the `public` schema will be accessible via the Supabase Data API. +Enable Row Level Security (RLS) on all tables and views you have exposed via the Data API. You can then write RLS policies to grant users access to specific database rows based on their authentication token. -To restrict access, enable Row Level Security (RLS) on all tables, views, and functions in the `public` schema. You can then write RLS policies to grant users access to specific database rows or functions based on their authentication token. +For functions, RLS does not apply. Instead, control access by granting `EXECUTE` privileges only to the roles that should be able to call the function, and review any `SECURITY DEFINER` functions carefully. -Always enable Row Level Security on tables, views, and functions in the `public` schema to protect your data. +Always enable Row Level Security on tables and views you expose via the Data API to protect your data. For functions, restrict access by granting `EXECUTE` only to appropriate roles. @@ -49,14 +49,10 @@ With RLS enabled, you can create Policies that allow or disallow users to access -Any table **without RLS enabled** in the `public` schema will be accessible to the public, using the `anon` role. Always make sure that RLS is enabled or that you've got other security measures in place to avoid unauthorized access to your project's data! +Any exposed table **without RLS enabled** can be accessed by roles with matching Data API grants (for example, `anon`). Always make sure RLS is enabled, or that you've got other controls in place to avoid unauthorized access to your project's data. -## Disable the API or restrict to custom schema - -If you don't use the Data API, or if you don't want to expose the `public` schema, you can either disable it entirely or change the automatically exposed schema to one of your choice. See **[Hardening the Data API](/docs/guides/api/hardening-data-api)** for instructions. - ## Enforce additional rules on each request Using Row Level Security policies may not always be adequate or sufficient to protect APIs. @@ -66,7 +62,7 @@ Here are some common situations where additional protections are necessary: - Enforcing per-IP or per-user rate limits. - Checking custom or additional API keys before allowing further access. - Rejecting requests after exceeding a quota or requiring payment. -- Disallowing direct access to certain tables, views or functions in the `public` schema. +- Disallowing direct access to certain tables, views, or functions in exposed schemas. You can build these cases in your application by creating a Postgres function that will read information from the request and perform additional checks, such as counting the number of requests received or checking that an API key is already registered in your database before serving the response. diff --git a/apps/docs/content/guides/database/data-api.mdx b/apps/docs/content/guides/database/data-api.mdx new file mode 100644 index 00000000000..3db9204f64e --- /dev/null +++ b/apps/docs/content/guides/database/data-api.mdx @@ -0,0 +1,47 @@ +--- +title: 'Data API' +description: 'Quick options for managing Data API exposure and access.' +--- + +The Supabase Data API is a standalone server that sits between your application client code and your database. It automatically generates a fully RESTful API based on your database structure, allowing you to interact with your database through HTTP endpoints. + +With the Data API, you have granular control over exposure: expose specific tables and functions by granting Data API roles the access they need, or enable **Default privileges for new entities** to automatically grant access to new tables and functions in `public`. + + + +Any table that is exposed through the Data API should have [Row Level Security (RLS) enabled](/docs/guides/database/postgres/row-level-security) to prevent unauthorized data access. + + + +## Expose specific tables and functions (recommended) + +In [Data API integrations settings](/dashboard/project/_/integrations/data_api/settings), expose specific tables and functions and grant only the privileges each role needs. + +```sql +grant select on table public.your_table to anon; +grant select, insert, update, delete on table public.your_table to authenticated; +grant execute on function public.your_function to anon, authenticated; +``` + +## Use default privileges for new entities in `public` + +If you want new entities in `public` to be accessible automatically, enable **Default privileges for new entities** in the [**Integrations > Data API**](/dashboard/project/_/integrations/data_api/settings) section of the Dashboard. This applies only to new tables and functions in `public`. + +```sql +alter default privileges for role postgres in schema public +grant select, insert, update, delete on tables to anon, authenticated, service_role; + +alter default privileges for role postgres in schema public +grant execute on functions to anon, authenticated, service_role; +``` + +## Disable the Data API completely + +If your app never uses Supabase client libraries, REST, or GraphQL data endpoints: + +1. In the [**Integrations > Data API**](/dashboard/project/_/integrations/data_api/overview) section of the Dashboard. +1. Turn **Enable Data API** off. + +## Learn more + +To learn more about the Data API, see the [full guide](/docs/guides/api). diff --git a/apps/docs/content/guides/database/full-text-search.mdx b/apps/docs/content/guides/database/full-text-search.mdx index 2a29ba1cba8..89358757126 100644 --- a/apps/docs/content/guides/database/full-text-search.mdx +++ b/apps/docs/content/guides/database/full-text-search.mdx @@ -665,7 +665,7 @@ select title from books where to_tsvector(title) @@ to_tsquery('Lit:*'); ### Extending functionality with RPC -To make the partial search functionality accessible through the API, you can wrap the search logic in a stored procedure. +To make the partial search functionality accessible through the API, you can wrap the search logic in a database function. After creating this function, you can invoke it from your application using the SDK for your platform. Here's an example: diff --git a/apps/docs/content/guides/database/postgres/data-deletion.mdx b/apps/docs/content/guides/database/postgres/data-deletion.mdx new file mode 100644 index 00000000000..685dcc44f80 --- /dev/null +++ b/apps/docs/content/guides/database/postgres/data-deletion.mdx @@ -0,0 +1,205 @@ +--- +id: 'data-deletion' +title: 'Deleting data and dropping objects safely' +description: 'Strategies for removing data and schema objects while minimising impact.' +footerHelpType: 'postgres' +--- + +Deleting rows and dropping database objects are routine operations, but on a live database they can lock tables, block queries, and cause downtime. This guide covers practical strategies for keeping these operations safe and fast. + +## Preparing to delete + +- Test in a staging environment +- Ensure you have a recent backup +- Confirm the table dependencies and foreign key constraints +- Drop dependent objects explicitly, use [CASCADE](/docs/guides/database/postgres/cascade-deletes) with caution +- Choose a low traffic time to run the operation +- Run operations inside a [migration](/docs/guides/deployment/database-migrations) +- Set timeouts, such as `lock_timeout` and `statement_timeout` + +### Identifying dependencies + +The system catalog tables `pg_class`, `pg_constraint`, and `pg_depend` can be used to identify dependencies: + +```sql +-- Find tables that depend on a specific table +select + d.classid::regclass as dependent_object, + d.objid::regclass as dependent_object_id, + d.refclassid::regclass as referenced_object, + d.refobjid::regclass as referenced_object_id +from pg_depend d +where d.refobjid = 'public.logs'::regclass; +``` + +If the object you want to delete has dependencies, you'll need to drop those first or use `CASCADE` which will automatically drop all related objects. + +## Data deletion strategies + +There are several ways to delete data from a table and the approach you choose depends on how much you want to delete. + +### Small deletes + +For tables with less than a few thousand rows, a `DELETE` operation is fine: + +```sql +delete from logs +where created_at < now() - interval '90 days'; +``` + +This acquires a `ROW EXCLUSIVE` lock on the table, which still allows other `SELECT`, `INSERT`, `UPDATE`, and `DELETE` statements to run concurrently. For small row counts, the operation completes quickly and has minimal impact. + +### Large deletes + +Deleting millions of rows in a single statement can hold locks for a long time, generate WAL (Write-Ahead Log) traffic, and impact replication. Instead, delete in batches: + +```sql +-- Delete 5,000 rows at a time +DELETE FROM logs +WHERE id IN ( + SELECT id + FROM logs + WHERE created_at < now() - interval '90 days' + LIMIT 5000 +); +``` + +This approach has the benefit of controlling when it runs, locking for a shorter period of time and minimising impact on other transactions. + +If you know in advance that such large deletes will have to happen in the business cycle of your database, then you should seriously think about using (table parititioning)[/docs/guides/database/partitions] as a management tool. + +### Soft deletes + +If you need to "delete" data but want the option to recover it, consider a soft-delete pattern: + +```sql +alter table orders +add column deleted_at timestamptz; + +-- "Delete" a row +update orders +set deleted_at = now() +where id = 42; +``` + +Then exclude soft-deleted rows in your queries or views: + +```sql +create view active_orders as + select * from orders where deleted_at is null; +``` + + + +Combine soft deletes with a scheduled hard-delete job (using [pg_cron](/docs/guides/database/extensions/pg_cron)) to permanently remove old soft-deleted rows in batches during low-traffic periods. + + + +### Deleting all data + +If you need to delete all data from a table, consider using `TRUNCATE` instead of `DELETE`: + +```sql +truncate table logs; +``` + +`TRUNCATE` is much faster than `DELETE` because it doesn't generate individual row-level WAL entries and doesn't scan the table. It also resets any auto-incrementing sequences. + +## Object deletion strategies + +### Dropping tables + +Dropping a table removes it and all its data permanently. Always use `IF EXISTS` to avoid errors in migrations: + +```sql +drop table if exists old_analytics; +``` + + + +`DROP TABLE` acquires an `ACCESS EXCLUSIVE` lock, which blocks **all** other operations on the table, including reads. On a busy table, this can queue up behind long-running queries. See [Monitoring locks](#monitoring-locks) below. + + + +### Dropping columns + +Dropping a column is a metadata-only operation in Postgres — it doesn't rewrite the table. However, it still requires an `ACCESS EXCLUSIVE` lock: + +```sql +alter table users +drop column if exists legacy_field; +``` + +Since the lock is brief (metadata-only), this is generally safe. But on a table with many concurrent transactions, even a brief `ACCESS EXCLUSIVE` lock can queue behind long-running queries. Use a lock timeout to avoid waiting indefinitely: + +```sql +set local lock_timeout = '5s'; +alter table users drop column if exists legacy_field; +``` + +If the statement times out, retry during a quieter period. + +### Dropping indexes + +Dropping a regular index takes an `ACCESS EXCLUSIVE` lock on the index but **not** on the table, so reads and writes to the table continue uninterrupted: + +```sql +drop index if exists idx_users_legacy_field; +``` + + + +The `inspect` command in the [Supabase CLI](/docs/reference/cli/supabase-inspect-db-index-stats) can help you identify unused indexes: + +```bash +supabase inspect db index-stats +``` + + + +## Monitoring + +### Check for blocked queries + +Query `pg_locks` and `pg_stat_activity` to see currently active queries and queries waiting for locks. + +The [Supabase CLI](/docs/reference/cli/supabase-inspect-db-locks) provides commands to view these metrics: + +```bash +supabase inspect db locks +supabase inspect db blocking +``` + +### Monitor table bloat after large deletes + +When deleting a large number of rows, the space is not always reclaimed and available for use. In normal cases, the rows are marked as deleted but the space is not immediately freed. You can monitor table bloat to see if the space is being reclaimed: + +```bash +supabase inspect db bloat +``` + +## Reclaiming disk space + +To reclaim the disk space freed by deleted rows, Postgres' autovacuum process runs automatically to mark deleted rows as reusable, but it may not always keep up with large deletes. + +If autovacuum is not keeping up, you can trigger a manual vacuum: + +```sql +vacuum (verbose) logs; +``` + +For reclaiming disk space (not just marking tuples as reusable), use `VACUUM FULL` — but be aware this rewrites the entire table and takes an `ACCESS EXCLUSIVE` lock: + +```sql +-- This locks the table for the duration — use during maintenance windows only +vacuum full logs; +``` + +The most efficient way to reclaim disk space, without locks, is to use [pg_repack](/docs/guides/database/extensions/pg_repack). + +## Related links + +- [Safe Cascading Deletes](/docs/guides/database/postgres/cascade-deletes) +- [Inspecting your Database](/docs/guides/database/inspect) +- [Understanding Database and Disk Size](/docs/guides/platform/database-size) +- [Bloat in Postgres](/docs/blog/postgres-bloat) diff --git a/apps/docs/content/guides/database/secure-data.mdx b/apps/docs/content/guides/database/secure-data.mdx index 3ddd3adef16..0b23425dda1 100644 --- a/apps/docs/content/guides/database/secure-data.mdx +++ b/apps/docs/content/guides/database/secure-data.mdx @@ -28,6 +28,6 @@ Supabase and Postgres provide you with multiple ways to manage security, includi - [Row Level Security](/docs/guides/database/postgres/row-level-security) - [Column Level Security](/docs/guides/database/postgres/column-level-security) -- [Hardening the Data API](/docs/guides/api/hardening-data-api) +- [Data API](/docs/guides/database/data-api) - [Managing Postgres roles](/docs/guides/database/postgres/roles) - [Managing secrets with Vault](/docs/guides/database/vault) diff --git a/apps/docs/content/guides/deployment/database-migrations.mdx b/apps/docs/content/guides/deployment/database-migrations.mdx index 3fd5cb5fd78..cc2333d0cbe 100644 --- a/apps/docs/content/guides/deployment/database-migrations.mdx +++ b/apps/docs/content/guides/deployment/database-migrations.mdx @@ -149,7 +149,7 @@ View the [complete code](https://github.com/supabase/supabase/tree/master/exampl ### Seeding data -Now that you are managing your database with migrations scripts, it would be great have some seed data to use every time you reset the database. +Now that you are managing your database with migrations, it would be great have some seed data to use every time you reset the database. @@ -200,6 +200,12 @@ You should now see the `employees` table, along with your seed data in the Dashb This workflow is great if you know SQL and are comfortable creating tables and columns. If not, you can still use the Dashboard to create tables and columns, and then use the CLI to diff your changes and create migrations. + + +Only use the Dashboard to make schema changes on your **local** database, then capture them with `supabase db diff`. Making schema changes directly on your **remote** database (via the SQL editor or Table Editor) bypasses the migration history and will cause `db push` to fail with sync errors. Once you're using migrations, all schema changes to your remote database should go through migration files only. + + + @@ -343,3 +349,146 @@ supabase db push --include-seed Visiting your live project on [Supabase](/dashboard/project/_), you'll see a new `employees` table, complete with the `department` column you added in the second migration above. + +## Working with a team + +When multiple developers share a Supabase project, a few rules keep migrations from getting out of sync. + +**The golden rule: never change the remote database directly.** Once you're using migrations, all schema changes — even small ones — should go through migration files. Using the Dashboard's SQL editor or Table Editor on your remote database bypasses the migration history, and `db push` will start failing with sync errors. + +**The team workflow:** + + + + + + Each developer creates migration files on their own branch, never touching the remote database directly. + + + + +```bash name=Terminal +supabase migration new your_change_description +``` + + + + + + + + + + + + Reset your local database to apply the migration, then commit the migration file to git. + + + + +```bash name=Terminal +supabase db reset +git add supabase/migrations +git commit -m "add migration: your_change_description" +``` + + + + + + + + + + + + After pulling new migration files from git, reset your local database to apply them. + + + + +```bash name=Terminal +git pull +supabase db reset +``` + + + + + + + + + + + + Coordinate so only one person runs `db push` at a time. Migration files are applied in timestamp order, so concurrent pushes from different machines can cause conflicts. + + + + +```bash name=Terminal +supabase db push +``` + + + + + + + + + +For a more automated deployment approach, consider using [Supabase Branching](/docs/guides/deployment/branching) or a CI/CD pipeline that runs `supabase db push` on merge to your main branch. + + + +## Diagnosing and fixing sync errors + +If `db push` fails with errors suggesting you run `supabase migration repair`, your local migration files and the remote database's migration history are out of sync. Here's how to diagnose and fix it. + +### How migration tracking works + +Supabase tracks which migrations have been applied on each database in a table called `supabase_migrations.schema_migrations`. When you run `supabase db push`, it compares your local `supabase/migrations` folder against that table and runs only the ones not yet applied, in order. + +Git tracks your migration _files_. Supabase tracks what's been _applied to each database_. These are two separate systems that need to stay in sync. + +### Step 1: Check what's out of sync + +Start by listing the migration status across local and remote: + +```bash name=Terminal +supabase migration list +``` + +This shows which migrations are applied locally, which are applied on the remote, and where they diverge. + +### Step 2: If you made changes on the remote database directly + +Pull the current remote state into a migration file to get back in sync: + +```bash name=Terminal +supabase db pull +``` + +This creates a new migration file capturing the current remote schema. Commit it to git, then follow the standard workflow going forward. + +### Step 3: If the migration history table is wrong + +If a migration shows as missing in the remote history table but the schema change is actually already there (for example, it was applied manually), you can mark it as applied without re-running it: + +```bash name=Terminal +supabase migration repair --status applied +``` + +Or if a migration is recorded as applied but was never actually run: + +```bash name=Terminal +supabase migration repair --status reverted +``` + + + +`migration repair` updates the tracking table only — it does not apply or revert any SQL. Use it to correct the history record when you know the actual database state is correct. + + diff --git a/apps/docs/content/guides/integrations/supabase-for-platforms.mdx b/apps/docs/content/guides/integrations/supabase-for-platforms.mdx index 5e14fe31e94..fdf84ffcd06 100644 --- a/apps/docs/content/guides/integrations/supabase-for-platforms.mdx +++ b/apps/docs/content/guides/integrations/supabase-for-platforms.mdx @@ -375,12 +375,6 @@ curl https://api.supabase.com/v1/projects/database/backups/restore-pitr \ Management API endpoint: [`GET /v1/oauth/authorize/project-claim`](https://api.supabase.com/api/v1#tag/oauth/get/v1/oauth/authorize/project-claim) - - -Only select customers have access to claim flow. Submit this [form](/solutions/ai-builders#talk-to-partnerships-team) to get access. - - - Your users may want to claim the project that currently lives in your org so that they can have more control over it. We've enabled transferring the project from your org to your user's org while you continue to retain access to interact with the project through an [OAuth integration](/docs/guides/integrations/build-a-supabase-oauth-integration). diff --git a/apps/docs/content/guides/platform/sso.mdx b/apps/docs/content/guides/platform/sso.mdx index 4315e7ddeb0..655ff458d1f 100644 --- a/apps/docs/content/guides/platform/sso.mdx +++ b/apps/docs/content/guides/platform/sso.mdx @@ -19,21 +19,64 @@ Supabase currently provides SAML SSO for [Team and Enterprise Plan customers](/p ## Supported providers -Supabase supports practically all identity providers that support the SAML 2.0 SSO protocol. We've prepared these guides for commonly used identity providers to help you get started. If you use a different provider, our support stands ready to support you. +Supabase supports practically all identity providers (IdP) that support the SAML 2.0 SSO protocol. These guides cover commonly used identity providers to help you get started. If you use a different provider, contact support. - [Google Workspaces (formerly G Suite)](/docs/guides/platform/sso/gsuite) - [Azure Active Directory](/docs/guides/platform/sso/azure) - [Okta](/docs/guides/platform/sso/okta) -Once configured, you can update your settings anytime via the [SSO tab](/dashboard/org/_/sso) under **Organization Settings**. +Once configured, you can update your settings anytime from [the **SSO** section](/dashboard/org/_/sso) of the dashboard under **Organization Settings**. -![SSO Example](/docs/img/sso-dashboard-enabled.png) +![SSO Example](/docs/img/sso-dashboard-enabled-idp.png) + + + +After configuring your SSO provider, thorough testing is essential. See our [SSO Testing and Best Practices](/docs/guides/platform/sso/testing-best-practices) guide for: + +- Step-by-step testing instructions +- Troubleshooting common issues +- Security best practices +- Pre-launch checklist + + + +## Choosing your login flow + +Supabase supports two SSO login flows: **IdP-initiated** and **SP-initiated**. You can enable one or both depending on your organization's needs. + +### IdP-initiated login (recommended) + +Users start their login from your identity provider (Okta, Azure AD, Google Workspace) by clicking an app tile or bookmark. This is the **simplest and most common configuration** - it requires no domain configuration and works automatically once SSO is enabled. + +**Best for:** + +- Organizations with established IdP workflows +- Multiple SAML apps per domain (Dev, Staging, Prod) +- Simplest user experience + +### SP-initiated login + +Users start their login at supabase.com by entering their email address, then are redirected to your identity provider. This flow requires configuring email domains to route users to the correct IdP. + +**Best for:** + +- Users who bookmark supabase.com directly +- Organizations migrating from password authentication +- Supporting domain-based automatic IdP routing + +### Need help choosing? + +- **Quick decision:** Start with IdP-initiated only (the default). It works for 90% of use cases. +- **Detailed guidance:** See [Choosing the Right Login Flow](/docs/guides/platform/sso/choosing-login-flow) for scenario-based recommendations. +- **Technical details:** Read [Understanding SSO Login Flows](/docs/guides/platform/sso/login-flows) for in-depth explanations. ## Key configuration options -- **Multiple domains** - You can associate one or more email domains with your SSO provider. Users with email addresses matching these domains are eligible to sign in via SSO. -- **Auto-join** - Optionally allow users with a matching domain to be added to your organization automatically when they first sign in, without an invitation. -- **Default role for auto-joined users** - Choose the role (e.g., `Read-only`, `Developer`, `Administrator`, `Owner`) that automatically joined users receive. Refer to [access control](/docs/guides/platform/access-control) for more information about roles. +- **Login flows** - Choose between IdP-initiated (users start from identity provider), SP-initiated (users start at supabase.com), or both. IdP-initiated is recommended for most organizations and requires no domain configuration. See [Choosing the Right Login Flow](/docs/guides/platform/sso/choosing-login-flow) for guidance. +- **Email domains** - Required only if you enable SP-initiated login. You can associate one or more email domains with your SSO provider. Users with matching email addresses can sign in via SSO at supabase.com. Not required for IdP-initiated flow. +- **Auto-join** - Optionally allow users with a matching domain to be added to your organization automatically when they sign in via SSO. Auto-join applies on every login, not just first signup, making it easy to test before enabling. +- **Default role for auto-joined users** - Choose the role (e.g., `Read-only`, `Developer`, `Administrator`, `Owner`) that automatically joined users receive. We recommend using `Developer` as the default (principle of least privilege) and promoting users individually as needed. Refer to [access control](/docs/guides/platform/access-control) for more information about roles. +- **Invitation types** - When inviting users to your organization, you can explicitly choose whether the invitation requires SSO authentication or allows non-SSO login (password/social). This enables mixed authentication organizations with both SSO and non-SSO users. ## How SSO works in Supabase @@ -47,13 +90,25 @@ When SSO is enabled for an organization: ## Enabling SSO for an organization -- Review the steps above to configure your setup. -- Invite users to the organization and ensure they join with their SSO linked account. -- If a user is already a member of the organization under a non SSO account, they will need to be removed and invited again for them to join under their SSO account. +**Recommended workflow:** - +1. Create or verify at least one non-SSO owner account exists (required for safety) +2. Configure your SSO provider following one of our [provider-specific guides](#supported-providers) +3. Start with auto-join **disabled** to test the configuration +4. Test SSO login with your own account +5. Once confirmed working, enable auto-join if desired +6. Thoroughly test using our [SSO Testing and Best Practices](/docs/guides/platform/sso/testing-best-practices) guide +7. Invite users to the organization or let them auto-join on login -**No automatic linking:** Each user account verified using a SSO identity provider will not be automatically linked to existing user accounts in the system. That is, if a user `valid.email@supabase.io` had signed up with a password, and then uses their company SSO login with your project, there will be two `valid.email@supabase.io` user accounts in the system. + + +If a user is already a member of the organization under a non-SSO account, they will need to be removed and invited again with an SSO-required invitation to join under their SSO account. SSO and non-SSO accounts with the same email are treated as separate accounts. + + + + + +Each user account verified using a SSO identity provider will not be automatically linked to existing user accounts in the system. That is, if a user `valid.email@supabase.io` had signed up with a password, and then uses their company SSO login with your project, there will be two `valid.email@supabase.io` user accounts in the system. Users will need to ensure they are logged in with the correct account when accepting invites or accessing organizations/projects. @@ -61,7 +116,19 @@ Users will need to ensure they are logged in with the correct account when accep ## Disabling SSO for an organization -If you disable the SSO provider for an organization, **all SSO users will immediately be unable to sign in**. Before disabling SSO, ensure you have at least one non-SSO owner account to prevent being locked out. +If you disable or delete the SSO provider for an organization, **all SSO users will immediately be unable to sign in**. + + + +The system requires at least one non-SSO owner account before allowing SSO provider deletion. This prevents complete organization lockout. When you delete an SSO provider, all SSO members are automatically removed from the organization. + +Before disabling or deleting SSO: + +- Verify a non-SSO owner account exists and can log in +- Communicate to affected users in advance +- Consider whether disabling is better than deleting if the change is temporary + + ## Removing an individual SSO user's access @@ -69,3 +136,25 @@ To revoke access for a specific SSO user without disabling the provider entirely - Remove or disable the user's account in your identity provider - Downgrade or remove their permissions for any organizations in Supabase. + +## Testing and best practices + +Before rolling out SSO to your organization, we strongly recommend thorough testing and following security best practices. Our comprehensive guide covers: + +- Step-by-step testing procedures for SSO login, auto-join, and invitations +- Troubleshooting common issues (many of which previously required support intervention) +- Security best practices including certificate monitoring and domain configuration +- Operational guidance for making SSO changes safely +- Pre-launch verification checklist + +Visit the [SSO Testing and Best Practices](/docs/guides/platform/sso/testing-best-practices) guide for complete details. + +## Advanced scenarios + +Most organizations use a single SSO provider for all users. However, Supabase supports multiple SSO providers within an organization for advanced use cases such as: + +- Separate providers for development, staging, and production environments +- Different providers for different teams or business units +- Gradual migration from one identity provider to another + +If you need to configure multiple SSO providers, refer to the [Multiple SSO Providers](/docs/guides/platform/sso/multiple-providers) guide for detailed configuration steps, and contact your Supabase support representative if you need additional guidance. diff --git a/apps/docs/content/guides/platform/sso/azure.mdx b/apps/docs/content/guides/platform/sso/azure.mdx index e914ce0658a..1794628df08 100644 --- a/apps/docs/content/guides/platform/sso/azure.mdx +++ b/apps/docs/content/guides/platform/sso/azure.mdx @@ -110,6 +110,12 @@ We do not permit use of public domains like `gmail.com`, `yahoo.com`. + + +You can configure each SSO provider with different email domains. For multi-environment setups (Dev/Staging/Prod), we recommend using IdP-initiated flow with multiple SAML apps under the same domain rather than domain-based routing. For more details, see the [Multiple SSO Providers guide](/docs/guides/platform/sso/multiple-providers). + + + ## Step 10: Configure metadata [#dashboard-configure-metadata] Enter the metadata URL you obtained from [Step 7](#idp-metadata-url) into the Metadata URL field: @@ -128,7 +134,7 @@ By default this setting is disabled, users logging in via SSO will not be added ![Auto-join disabled](/docs/img/sso-dashboard-configure-autojoin-disabled.png) -Toggle this on if you want SSO-authenticated users to be **automatically added to your organization** when they log in via SSO. +Toggle this on if you want SSO-authenticated users to be **automatically added to your organization** when they log in via SSO. Auto-join applies on **every login**, not just first signup - this makes it safe to test SSO before enabling this feature. ![Auto-join enable](/docs/img/sso-dashboard-configure-autojoin-enabled.png) @@ -136,18 +142,30 @@ When auto-join is enabled, you can choose the **default role** for new users: ![Auto-join role selection](/docs/img/sso-dashboard-configure-autojoin-enabled-role.png) -Choose a role that fits the level of access you want to grant to new members. +We recommend choosing **Developer** as the default role (principle of least privilege) and promoting users individually as needed. -Visit [access-control](/docs/guides/platform/access-control) documentation for details about each role. +Read [the Access Control documentation](/docs/guides/platform/access-control) for details about each role. -## Step 13: Save changes and test single sign-on [#dashboard-configure-save] +## Step 13: Save changes [#dashboard-configure-save] When you click **Save changes**, your new SSO configuration is applied immediately. From that moment, any user with an email address matching one of your configured domains who visits your organization's sign-in URL will be routed through the SSO flow. -We recommend asking a few users to test signing in via their Azure AD account. They can do this by entering their email address on the [Sign in with SSO](/dashboard/sign-in-sso) page. +## Step 14: Test your SSO configuration -If SSO sign-in doesn't work as expected, contact your Supabase support representative for assistance. +Before rolling out SSO to your organization, we strongly recommend thorough testing. Read [the SSO Testing and Best Practices guide](/docs/guides/platform/sso/testing-best-practices) for: + +- Step-by-step testing instructions +- How to verify auto-join works correctly +- Common issues and troubleshooting +- Security best practices +- Pre-launch checklist + + + +If your organization has an Azure sandbox or test tenant, consider testing your SSO configuration there first before applying to production. + + diff --git a/apps/docs/content/guides/platform/sso/choosing-login-flow.mdx b/apps/docs/content/guides/platform/sso/choosing-login-flow.mdx new file mode 100644 index 00000000000..986bdbce3d5 --- /dev/null +++ b/apps/docs/content/guides/platform/sso/choosing-login-flow.mdx @@ -0,0 +1,320 @@ +--- +title: 'Choosing the Right SSO Login Flow' +description: 'Quick reference guide to help you choose between IdP-initiated, SP-initiated, or both login flows based on your use case.' +--- + +Not sure which single sign-on (SSO) login flow to enable? This guide maps common enterprise scenarios to the recommended configuration. + + + +Start with identity provider (IdP)-initiated, the default behavior. It requires no domain configuration, and works for most enterprise use cases. You can enable SP-initiated later if needed. + + + +## Decision flowchart + +``` +Do users need to start login at supabase.com? +│ +├─ No → Use IdP-initiated only (default) ✅ +│ - No domain configuration needed +│ - Simplest setup +│ - Users access via IdP dashboard +│ +└─ Yes → Do you need multiple SAML apps per domain? + │ + ├─ Yes → Use IdP-initiated only ✅ + │ - Supports Dev/Staging/Prod under same domain + │ - Each environment is a separate IdP tile + │ + └─ No → Enable both flows ✅ + - SP-initiated for users who bookmark supabase.com + - IdP-initiated still works from IdP dashboard + - Configure email domains +``` + +## Common scenarios + +### Scenario 1: Multiple environments (dev, staging, prod) + +**Your situation:** + +- You need separate Supabase organizations for Dev, Staging, and Production +- All employees use `company.com` email addresses +- You can't assign different email domains to different environments + +**Recommended configuration:** IdP-initiated only ✅ + +**Why:** + +- Create separate SAML apps in your IdP for each environment +- All apps use the same domain (`company.com`) +- Users click "Supabase Dev", "Supabase Staging", or "Supabase Prod" tiles +- No domain conflicts + +**How to configure:** + +1. In your IdP, create three SAML apps: + - "Supabase Dev" → Points to dev org ACS URL + - "Supabase Staging" → Points to staging org ACS URL + - "Supabase Production" → Points to prod org ACS URL +2. In each Supabase organization: + - Enable SSO + - Leave "Enable SP-initiated login" **OFF** + - Configure metadata from corresponding IdP app +3. Users access each environment via IdP tiles + +**Result:** Clean separation of environments with single domain. + +--- + +### Scenario 2: Single production organization + +**Your situation:** + +- One Supabase organization for your entire company +- All employees use company email domain +- Users are comfortable with IdP dashboard + +**Recommended configuration:** IdP-initiated only ✅ + +**Why:** + +- Simplest possible setup +- No domain configuration required +- Users access Supabase with one click from IdP +- Fewer potential failure points + +**How to configure:** + +1. Enable SSO in your Supabase organization +2. Leave "Enable SP-initiated login" **OFF** +3. Configure identity provider metadata +4. Create Supabase app tile in your IdP + +**Result:** One-click SSO login for all users. + +--- + +### Scenario 3: Users bookmark supabase.com + +**Your situation:** + +- Users frequently bookmark supabase.com directly +- You want to support starting login from Supabase +- Single domain, single organization + +**Recommended configuration:** Enable both flows ✅ + +**Why:** + +- Supports users who start at supabase.com (SP-initiated) +- Also supports users who prefer IdP tiles (IdP-initiated) +- Flexible for different user preferences + +**How to configure:** + +1. Enable SSO in your Supabase organization +2. Toggle "Enable SP-initiated login" **ON** +3. Add your email domain(s) (e.g., `company.com`) +4. Configure identity provider metadata +5. Create Supabase app tile in your IdP (optional but recommended) + +**Result:** Users can start login from either Supabase or IdP. + +--- + +### Scenario 4: Migrating from password authentication + +**Your situation:** + +- Currently using password-based login +- Transitioning to SSO +- Users are used to starting at supabase.com + +**Recommended configuration:** Enable both flows ✅ + +**Why:** + +- Familiar login starting point for existing users +- Gradual transition to IdP-based access +- Can promote IdP tiles after users adapt + +**How to configure:** + +1. Ensure at least one non-SSO owner account exists +2. Enable SSO and toggle "Enable SP-initiated login" **ON** +3. Add email domain(s) +4. Start with auto-join **disabled** +5. Test with small group +6. Enable auto-join once confirmed +7. Communicate new IdP tiles to users +8. Gradually encourage IdP-initiated usage + +**Migration path:** + +- **Week 1:** Enable both flows, announce SSO availability +- **Week 2-4:** Monitor usage, troubleshoot issues +- **Month 2+:** Promote IdP tiles, consider disabling SP-initiated if usage drops + +--- + +### Scenario 5: Multiple subsidiaries with different domains + +**Your situation:** + +- Parent company (`parent.com`) and subsidiaries (`sub1.com`, `sub2.com`) +- All use the same Supabase organization +- Each domain maps to same identity provider + +**Recommended configuration:** SP-initiated with multiple domains ✅ + +**Why:** + +- Multiple domains supported in SP-initiated configuration +- Automatic routing based on email domain +- Centralized organization management + +**How to configure:** + +1. Enable SSO in your Supabase organization +2. Toggle "Enable SP-initiated login" **ON** +3. Add all domains: `parent.com`, `sub1.com`, `sub2.com` +4. Configure identity provider to accept all domains +5. Create Supabase app tiles in IdP (IdP-initiated also works) + +**Result:** Users with any configured domain can access organization. + +--- + +### Scenario 6: SaaS platform with customer-specific SSO + +**Your situation:** + +- You're building a SaaS product +- Each customer has their own organization +- Each customer uses their own identity provider + +**Recommended configuration:** Per-customer decision (typically IdP-initiated) + +**Why:** + +- Each customer may have different preferences +- Default to IdP-initiated for simplicity +- Enable SP-initiated only if customer requests it + +**How to configure:** + +1. For each customer organization: + - Enable SSO with their IdP metadata + - Default: Leave SP-initiated **OFF** + - If customer requests SP-initiated: Toggle **ON** and add their domain +2. Document both options in customer onboarding +3. Let customers choose based on their workflow + +**Result:** Flexible, customer-specific SSO configurations. + +--- + +### Scenario 7: Mixed authentication (SSO + non-SSO users) + +**Your situation:** + +- Some users authenticate via SSO (employees) +- Some users use password/social auth (contractors, external partners) +- Single organization with mixed membership + +**Recommended configuration:** Both flows with careful planning ⚠️ + +**Why:** + +- SSO users can use either flow +- Non-SSO users use password/social login +- Separate invitation types for each group + +**How to configure:** + +1. Enable SSO with both flows (toggle SP-initiated **ON**) +2. Configure employee email domain(s) +3. Use **SSO-required invitations** for employees +4. Use **non-SSO invitations** for contractors +5. Consider disabling auto-join to control membership + + + +SSO and non-SSO accounts with the same email are treated as separate accounts. An employee with `alice@company.com` will have two accounts if they: + +1. Join via SSO (SSO account) +2. Previously joined via password (non-SSO account) + +Communicate clearly which authentication method each user should use. + + + +**Result:** Mixed authentication with clear separation. + +## Configuration quick reference + +| Use Case | IdP-initiated | SP-initiated | Domains Required | +| ---------------------------------------- | ------------- | ------------ | ----------------- | +| Multiple environments (Dev/Staging/Prod) | ✅ Only | ❌ Off | No | +| Single production org | ✅ Only | ❌ Off | No | +| Users bookmark supabase.com | ✅ Yes | ✅ Yes | Yes | +| Migrating from passwords | ✅ Yes | ✅ Yes | Yes | +| Multiple email domains | ✅ Yes | ✅ Yes | Yes (all domains) | +| Customer-specific SSO (SaaS) | ✅ Default | Optional | Per customer | +| Mixed authentication | ✅ Yes | ✅ Yes | Yes | + +## Testing your configuration + +After choosing your login flow, thoroughly test: + +1. **IdP-initiated:** Click app tile in IdP → Verify redirect to Supabase +2. **SP-initiated:** Go to supabase.com/sign-in-sso → Enter email → Verify IdP redirect +3. **Auto-join:** Test with new user accounts +4. **Domain restrictions:** Try non-matching domain (should fail for SP-initiated) + +See our comprehensive [SSO Testing and Best Practices guide](/docs/guides/platform/sso/testing-best-practices) for detailed testing procedures. + +## When to change configuration + +You can safely change login flow configuration at any time: + +### Adding SP-initiated to IdP-only + +- Toggle "Enable SP-initiated login" ON +- Add required domains +- Test with existing users +- No disruption to IdP-initiated flow + +### Removing SP-initiated + +- Toggle "Enable SP-initiated login" OFF +- Domains are preserved (can re-enable later) +- IdP-initiated continues working +- Users who bookmarked supabase.com need to use IdP tiles instead + +**No migration required** - Changes take effect immediately. + +## Still not sure? + +If you're uncertain which configuration to use: + +1. Start with IdP-initiated only (simplest, works for most cases) +2. Test with a small group of users +3. Gather feedback on user experience +4. Enable SP-initiated if users request it +5. Monitor usage to see which flow is preferred + + + +If you need help choosing the right configuration for your organization, contact Supabase support with details about your use case. We're happy to provide personalized recommendations. + + + +## Next steps + +- **Understand the technical details:** Read [Understanding SSO Login Flows](/docs/guides/platform/sso/login-flows) +- **Configure your provider:** Follow our [provider-specific guides](/docs/guides/platform/sso#supported-providers) +- **Test your setup:** Review [SSO Testing and Best Practices](/docs/guides/platform/sso/testing-best-practices) +- **Enable auto-join:** Configure [auto-join settings](/docs/guides/platform/sso#key-configuration-options) diff --git a/apps/docs/content/guides/platform/sso/gsuite.mdx b/apps/docs/content/guides/platform/sso/gsuite.mdx index 69dbbc86bd4..4babba24020 100644 --- a/apps/docs/content/guides/platform/sso/gsuite.mdx +++ b/apps/docs/content/guides/platform/sso/gsuite.mdx @@ -39,7 +39,13 @@ This is a very important step. Click on _DOWNLOAD METADATA_ and save the file th ![Google Workspace: Web and mobile apps admin console, Add custom SAML, Google Identity Provider details screen](/docs/img/sso-gsuite-step-04.png) -**Important: Make sure the certificate as shown on screen has at least 1 year before it expires. Mark down this date in your calendar so you will be reminded that you need to update the certificate without any downtime for your users.** +**Important: Make sure the certificate as shown on screen has at least 1 year before it expires.** + + + +**Certificate expiration:** Set a calendar reminder 30 days before the certificate expiration date. When the certificate is renewed, you'll need to download the new metadata file and update it in your Supabase SSO settings. Expired certificates are a common cause of SSO sign-in failures. + + ## Step 5: Add service provider details [#add-service-provider-details] @@ -102,6 +108,12 @@ We do not permit use of public domains like `gmail.com`, `yahoo.com`. + + +Each SSO provider can be configured with different email domains. For multi-environment setups (Dev/Staging/Prod), we recommend using IdP-initiated flow with multiple SAML apps under the same domain rather than domain-based routing. For more details, see the [Multiple SSO Providers guide](/docs/guides/platform/sso/multiple-providers). + + + ## Step 10: Configure metadata [#dashboard-configure-metadata] Upload the metadata file you downloaded in [Step 6](#download-idp-metadata) into the Metadata Upload File field. @@ -122,11 +134,17 @@ If you did not customize your settings you may save some time by clicking the ** ## Step 12: Join organization on signup (optional) [#dashboard-configure-autojoin] + + +**Recommended workflow:** Start with auto-join **disabled** to test your SSO configuration. Once SSO login is working correctly, enable auto-join if desired. + + + By default this setting is disabled, users logging in via SSO will not be added to your organization automatically. ![Auto-join disabled](/docs/img/sso-dashboard-configure-autojoin-disabled.png) -Toggle this on if you want SSO-authenticated users to be **automatically added to your organization** when they log in via SSO. +Toggle this on if you want SSO-authenticated users to be **automatically added to your organization** when they log in via SSO. Auto-join applies on **every login**, not just first signup - this makes it safe to test SSO before enabling this feature. ![Auto-join enable](/docs/img/sso-dashboard-configure-autojoin-enabled.png) @@ -134,7 +152,7 @@ When auto-join is enabled, you can choose the **default role** for new users: ![Auto-join role selection](/docs/img/sso-dashboard-configure-autojoin-enabled-role.png) -Choose a role that fits the level of access you want to grant to new members. +We recommend choosing **Developer** as the default role (principle of least privilege) and promoting users individually as needed. @@ -142,10 +160,20 @@ Visit [access-control](/docs/guides/platform/access-control) documentation for d -## Step 13: Save changes and test single sign-on [#dashboard-configure-save] +## Step 13: Save changes [#dashboard-configure-save] When you click **Save changes**, your new SSO configuration is applied immediately. From that moment, any user with an email address matching one of your configured domains who visits your organization's sign-in URL will be routed through the SSO flow. -We recommend asking a few users to test signing in via their Google Workspace account. They can do this by entering their email address on the [Sign in with SSO](/dashboard/sign-in-sso) page. + -If SSO sign-in doesn't work as expected, contact your Supabase support representative for assistance. +**Next step: Test your SSO configuration** + +Before rolling out SSO to your organization, we strongly recommend thorough testing. Visit our [SSO Testing and Best Practices](/docs/guides/platform/sso/testing-best-practices) guide for: + +- Step-by-step testing instructions +- How to verify auto-join works correctly +- Common issues and troubleshooting +- Security best practices +- Pre-launch checklist + + diff --git a/apps/docs/content/guides/platform/sso/login-flows.mdx b/apps/docs/content/guides/platform/sso/login-flows.mdx new file mode 100644 index 00000000000..54db12195a8 --- /dev/null +++ b/apps/docs/content/guides/platform/sso/login-flows.mdx @@ -0,0 +1,267 @@ +--- +title: 'Understanding SSO Login Flows' +description: 'Learn about IdP-initiated and SP-initiated SSO login flows and when to use each approach.' +--- + +When configuring SSO for your organization, you can choose between two different login flows: **identity provider (IdP)-initiated** and **service provider (SP)-initiated**. Understanding the difference helps you provide the best experience for your users. + + + +Most enterprises use IdP-initiated flow for its simplicity and better user experience. Enable SP-initiated only if you need users to start their login journey at supabase.com. + +See our [Choosing the Right Login Flow guide](/docs/guides/platform/sso/choosing-login-flow) for use case examples. + + + +## Overview of login flows + +### IdP-initiated (Identity Provider Initiated) + +With IdP-initiated flow, users start their login journey from your identity provider (Okta, Azure AD, Google Workspace, etc.) and are directly authenticated into Supabase. + +**User experience:** + +1. User opens their identity provider dashboard (e.g., Okta homepage, Azure MyApps) +2. User clicks the Supabase app tile or bookmark +3. User is immediately logged into Supabase (if already authenticated with IdP) + +**Key characteristics:** + +- ✅ Simpler user experience - one click from IdP +- ✅ No domain configuration required +- ✅ Works automatically once SSO is enabled +- ✅ Better for intranet portals and employee app catalogs +- ✅ Default behavior in Supabase + +### SP-initiated (Service Provider Initiated) + +With SP-initiated flow, users start at supabase.com, enter their email address, and are redirected to your identity provider for authentication. + +**User experience:** + +1. User visits supabase.com and clicks "Sign in with SSO" +2. User enters their email address +3. User is redirected to their identity provider +4. After authenticating, user is redirected back to Supabase + +**Key characteristics:** + +- ✅ Familiar flow for users who bookmark supabase.com +- ✅ Supports domain-based automatic IdP routing +- ⚠️ Requires configuring email domains +- ⚠️ More steps in the login process + +## Choosing between flows + +### When to use IdP-initiated (recommended) + +**Best for:** + +- Organizations with established identity provider workflows +- Users who primarily access apps through their IdP dashboard +- Multiple SAML apps per domain (Dev, Staging, Prod environments) +- Simplifying user onboarding + +**Common scenarios:** + +- "Our team accesses all tools through Okta tiles" +- "We want the simplest possible login experience" +- "We need separate Dev and Prod SAML apps under the same domain" +- "Users should never need to remember supabase.com" + +### When to use SP-initiated + +**Best for:** + +- Organizations where users bookmark supabase.com directly +- Migrating from password-based authentication +- Users unfamiliar with identity provider dashboards + +**Common scenarios:** + +- "Some users bookmark supabase.com and expect to start there" +- "We're transitioning from password auth to SSO" +- "Users need a consistent login page across all tools" +- "We want domain-based automatic IdP selection" + +### When to enable both flows + +You can enable both flows simultaneously to support different user preferences. + +**Best for:** + +- Large organizations with diverse user needs +- Gradual SSO migration with mixed authentication +- Supporting both technical and non-technical users + +## Configuring login flows + +### Enabling IdP-initiated flow (default) + +IdP-initiated flow is automatically enabled when you configure SSO. No additional steps required. + +1. Navigate to [the **SSO** settings](/dashboard/org/_/sso) section of the dashboard +2. Enable "Single Sign-On" +3. Configure your identity provider metadata and attribute mapping +4. Save your configuration + +Users can now access Supabase through your IdP's app catalog. + + + +With IdP-initiated flow, you don't need to configure email domains. Your identity provider handles all authentication routing. + + + +### Enabling SP-initiated flow + +To enable SP-initiated flow, you need to configure email domains: + +1. Navigate to [the **SSO** settings](/dashboard/org/_/sso) section of the dashboard +2. Enable "Single Sign-On" +3. Toggle **Enable SP-initiated login** to "ON" +4. Add one or more email domains (e.g., `yourcompany.com`) +5. Configure your identity provider metadata and attribute mapping +6. Save your configuration + +#### Email domain requirements + +- At least one domain required when SP-initiated is enabled +- Domains must be verified through your identity provider +- Multiple domains supported (e.g., `company.com`, `subsidiary.com`) +- Users with matching email domains will be routed to your IdP + + + +Only users with email addresses matching your configured domains can use SP-initiated login. Users with other domains cannot sign in via SSO at supabase.com (but can still use IdP-initiated flow if you configure it in your IdP). + + + +### Switching between flows + +You can change login flow configuration at any time: + +#### To switch from SP-initiated to IdP-only + +1. Navigate to [the **SSO** settings](/dashboard/org/_/sso) section of the dashboard +2. Toggle **Enable SP-initiated login** to "OFF" +3. Save changes + +Existing users can continue signing in via IdP-initiated flow. + +#### To switch from IdP-only to SP-initiated + +1. Navigate to [the **SSO** settings](/dashboard/org/_/sso) section of the dashboard +2. Toggle **Enable SP-initiated login** to "ON" +3. Add required email domains +4. Save changes + +## Technical details + +### How IdP-initiated flow works + +1. User clicks app tile in identity provider +2. IdP generates SAML assertion and POSTs to Supabase ACS URL +3. Supabase validates assertion and creates session +4. User is redirected to Supabase dashboard + +**No domain lookup required** - The IdP assertion contains all necessary user information. + +### How SP-initiated flow works + +1. User enters email at supabase.com/sign-in-sso +2. Supabase matches email domain to configured SSO provider +3. Supabase generates SAML request and redirects to IdP +4. IdP authenticates user and generates SAML assertion +5. IdP POSTs assertion to Supabase ACS URL +6. Supabase validates assertion and creates session + +**Domain matching is critical** - Without matching domains, users cannot complete SP-initiated flow. + +## Multiple SAML apps per domain + +One of the key advantages of IdP-initiated flow is supporting multiple SAML applications under the same domain. + +### The problem with SP-initiated only + +Many enterprises need separate SAML apps for different environments: + +- Development SAML app +- Staging SAML app +- Production SAML app + +**With SP-initiated flow only:** Each SAML app requires a unique domain. You'd need: + +- `dev.company.com` +- `staging.company.com` +- `prod.company.com` + +This is often impractical since all employees use `company.com` email addresses. + +### The solution with IdP-initiated flow + +**With IdP-initiated flow:** All SAML apps can use the same domain (`company.com`) because: + +- Users access each app through different IdP tiles/bookmarks +- No domain-based routing is needed +- Each SAML app has its own unique ACS URL and metadata + +#### Configuration in your IdP + +- Create "Supabase Dev" SAML app → Points to dev org's ACS URL +- Create "Supabase Staging" SAML app → Points to staging org's ACS URL +- Create "Supabase Production" SAML app → Points to prod org's ACS URL + +Users click the appropriate tile for the environment they need. + + + +This is the recommended approach for enterprises with multiple environments. Configure each environment as IdP-initiated only (no domains needed). + + + +## Common questions + +### Can you use both flows simultaneously? + +Yes! Enable SP-initiated login and configure domains. IdP-initiated flow continues to work automatically. + +### What happens when you don't configure domains? + +Without domains, only IdP-initiated flow is available. Users cannot start their login at supabase.com. + +### Does the IdP require configuration? + +For **IdP-initiated flow:** Configure the Supabase ACS URL and entity ID in your IdP. See our provider-specific guides: + +- [Google Workspace](/docs/guides/platform/sso/gsuite) +- [Azure Active Directory](/docs/guides/platform/sso/azure) +- [Okta](/docs/guides/platform/sso/okta) + +For **SP-initiated flow:** Same configuration, but also ensure your IdP accepts SAML requests from Supabase. + +### What happens if a user tries SP-initiated with no matching domain? + +They receive an error message indicating no SSO provider found for their email domain. They can still sign in using password or social auth (if they have a non-SSO account). + +### Can you disable SP-initiated flow after enabling it? + +Yes, toggle it off at any time. Existing users can continue using IdP-initiated flow. + +### Which flow is more secure? + +Both flows are equally secure when properly configured. Security depends on: + +- Strong identity provider authentication policies +- Certificate management and rotation +- Attribute mapping configuration +- Regular security audits + +See our [SSO Testing and Best Practices](/docs/guides/platform/sso/testing-best-practices) guide for security recommendations. + +## Next steps + +- **Choose your login flow:** See [Choosing the Right Login Flow](/docs/guides/platform/sso/choosing-login-flow) +- **Configure your provider:** Follow our [provider-specific guides](/docs/guides/platform/sso#supported-providers) +- **Test thoroughly:** Review [SSO Testing and Best Practices](/docs/guides/platform/sso/testing-best-practices) +- **Enable auto-join:** Configure [auto-join settings](/docs/guides/platform/sso#key-configuration-options) for seamless onboarding diff --git a/apps/docs/content/guides/platform/sso/multiple-providers.mdx b/apps/docs/content/guides/platform/sso/multiple-providers.mdx new file mode 100644 index 00000000000..1951ba2cba8 --- /dev/null +++ b/apps/docs/content/guides/platform/sso/multiple-providers.mdx @@ -0,0 +1,546 @@ +--- +title: 'Multiple SSO Providers' +description: 'Configure multiple SSO providers for different environments, teams, or use cases' +--- + +Many enterprises need multiple single sign-on (SSO) providers configured within Supabase to support different environments, teams, or organizational structures. This guide explains when and how to set up multiple providers effectively. + +## Why multiple SSO providers? + +Common scenarios requiring multiple SSO providers include: + +- **Multiple environments**: Separate Dev, Staging, and Production organizations +- **Team separation**: Different business units or departments +- **Migration**: Transitioning from one identity provider to another +- **Acquisitions**: Integrating subsidiaries with different identity systems +- **Testing**: Isolated test environments alongside production + +## Key concept: IDP-initiated enables unlimited providers per domain + +The traditional challenge with multiple SAML apps is domain conflicts. With SP-initiated flow only, each SAML app requires a unique email domain. Since all your employees use the same domain (e.g., `company.com`), this creates a problem. + +**Solution:** Use identity provider (IdP)-initiated flow, which doesn't require domain configuration. You can create unlimited SAML apps under the same domain. + + + +Configure each environment as IdP-initiated only (no domains). Users access each environment through different app tiles in your identity provider. + +For technical details, see [the Understanding SSO Login Flows guide](/docs/guides/platform/sso/login-flows#multiple-saml-apps-per-domain). + + + +## Use case 1: Multiple environments (dev/staging/prod) + +This is the most common enterprise pattern and the primary use case for IDP-initiated flow. + +### The challenge + +You have three Supabase organizations: + +- Development (`dev-org`) +- Staging (`staging-org`) +- Production (`prod-org`) + +All employees use `company.com` email addresses. You need separate SSO configurations for each environment. + +### The solution + +Create three separate SAML apps in your identity provider, each pointing to a different Supabase organization. + +#### In your identity provider (Okta, Azure AD, Google Workspace) + +1. **Create "Supabase Dev" SAML app** + - Configure with Dev organization's ACS URL + - Assign developers and testers + - Deploy app tile labeled "Supabase Dev" + +2. **Create "Supabase Staging" SAML app** + - Configure with Staging organization's ACS URL + - Assign QA team and release managers + - Deploy app tile labeled "Supabase Staging" + +3. **Create "Supabase Production" SAML app** + - Configure with Production organization's ACS URL + - Assign production users only + - Deploy app tile labeled "Supabase Production" + +#### In each Supabase organization + +1. Navigate to [the **SSO** settings](/dashboard/org/_/sso) section of the dashboard +2. Enable **Single Sign-On** +3. Leave **Enable SP-initiated login** "OFF", this is critical +4. Configure metadata from the corresponding IDP app +5. Set up attribute mappings +6. Configure auto-join if desired + +### Result + +- Users click the appropriate app tile for the environment they need +- No domain conflicts (all apps use `company.com`) +- Clean isolation between environments +- Each environment can have different user assignments and roles + +### Configuration example + +Here's how the three configurations differ: + +| Environment | Organization | IDP App Name | ACS URL | +| ----------- | ------------ | ------------------ | ------------------------------------ | +| Dev | `dev` | "Supabase Dev" | `https://...dev-org.../saml/acs` | +| Staging | `staging` | "Supabase Staging" | `https://...staging-org.../saml/acs` | +| Production | `prod` | "Supabase Prod" | `https://...prod-org.../saml/acs` | + +#### Each organization + +- SP-initiated: **OFF** (no domains configured) +- IDP-initiated: **ON** (automatic, no configuration needed) +- Auto-join: Your preference (typically enabled for dev, disabled for prod) + +## Use case 2: Different teams or business units + +Some organizations need to isolate teams within separate Supabase organizations. + +### Example scenario + +- Engineering team uses `engineering-org` +- Data team uses `data-org` +- Both teams have `company.com` emails + +### Configuration approach + +#### Option A: IDP-initiated with different app tiles + +Create separate SAML apps in your IDP: + +- "Supabase Engineering" → Points to `engineering-org` +- "Supabase Data" → Points to `data-org` + +Assign appropriate users to each app. Users only see the tiles they're assigned to. + +#### Option B: Both IDP and SP-initiated with role-based routing + +Configure both organizations with SP-initiated enabled using the same domain: + +- Both organizations add `company.com` as a domain +- Users can log in via SP-initiated at supabase.com +- System routes based on org membership (first match wins) +- Also provide IDP tiles for explicit routing + + + +When multiple organizations use SP-initiated with the same domain, the first provider where the user is a member will be used. This can cause confusion. **IDP-initiated is recommended** for clarity. + + + +## Use case 3: Migration from one IDP to another + +When migrating from one identity provider to another, multiple providers help ensure a smooth transition. + +### Migration workflow + +#### Phase 1: Dual configuration + +1. Configure new IDP as additional SSO provider +2. Keep existing IDP active +3. Test new IDP with small group +4. Both providers operational simultaneously + +#### Phase 2: Gradual rollout + +1. Migrate users in batches to new IDP +2. Update app tile assignments +3. Monitor for issues +4. Keep old IDP as fallback + +#### Phase 3: Cutover + +1. Move all users to new IDP +2. Verify no users depend on old IDP +3. Disable (don't delete) old IDP provider +4. Monitor for any issues + +#### Phase 4: Cleanup + +1. After verification period (1-2 weeks) +2. Delete old IDP provider +3. Update documentation + + + +Always maintain at least one non-SSO owner account during migrations to ensure you never lose access to the organization. + + + +## Use case 4: Acquisitions and subsidiaries + +Organizations with multiple email domains need provider configurations for each domain. + +### Example scenario + +- Parent company: `parent.com` +- Subsidiary 1: `subsidiary1.com` +- Subsidiary 2: `subsidiary2.com` + +All authenticate through the same central IDP but use different email domains. + +### Configuration approach + +#### Option A: Single provider with multiple domains (SP-initiated) + +1. Enable SSO with SP-initiated flow +2. Add all domains: `parent.com`, `subsidiary1.com`, `subsidiary2.com` +3. Configure single IDP metadata +4. Users with any matching domain can log in via supabase.com + +#### Option B: Separate providers per subsidiary (IDP-initiated) + +1. Create separate SAML apps for each entity +2. Configure as IDP-initiated only +3. Assign users based on their subsidiary +4. More isolation, clearer organization boundaries + +## Step-by-step setup guide + +### For IDP-initiated multi-environment pattern + +This is the recommended pattern for most enterprises. + +#### Step 1: Plan your environments + +Document: + +- Organization names and slugs +- Environment purposes (dev, staging, prod) +- Which users need access to which environments +- Default roles for each environment + +#### Step 2: Create SAML apps in your IDP + +For each environment, follow your provider-specific guide to create a SAML app: + +- [Okta setup guide](/docs/guides/platform/sso/okta) +- [Azure AD setup guide](/docs/guides/platform/sso/azure) +- [Google Workspace setup guide](/docs/guides/platform/sso/gsuite) + +#### Naming convention example + +- "Supabase - Production" +- "Supabase - Staging" +- "Supabase - Development" + +Use consistent naming to help users identify the right environment. + +#### Step 3: Configure each Supabase organization + +For **each** organization: + +1. Navigate to [the **SSO** settings](/dashboard/org/_/sso) section of the dashboard +2. Enable **Single Sign-On** +3. Verify **Enable SP-initiated login** is "OFF" +4. Upload or paste metadata from the corresponding IDP app +5. Configure attribute mappings: + - Email (required): Map to `email` + - Name (optional): Map to `name` or `displayName` +6. Configure auto-join settings: + - Dev: Usually enabled with "Developer" role + - Staging: Usually enabled with "Developer" role + - Prod: Usually disabled (explicit invitations only) +7. Save configuration + +#### Step 4: Test each environment separately + +For each environment: + +1. Open your IDP dashboard (Okta, Azure, Google) +2. Click the corresponding app tile +3. Verify redirect to correct Supabase organization +4. Check that user information is populated correctly +5. Verify auto-join behavior (if enabled) + +See [the SSO Testing and Best Practices guide](/docs/guides/platform/sso/testing-best-practices) for comprehensive testing procedures. + +#### Step 5: Assign users in your IDP + +Configure app assignments in your IDP: + +- **Production**: Only production users (restrictive) +- **Staging**: QA team, release managers, senior engineers +- **Development**: All engineers and testers (permissive) + +Users only see app tiles they're assigned to. + +#### Step 6: Document and communicate + +Create documentation for your team: + +- Which app tile corresponds to which environment +- Access request process for each environment +- Naming conventions and organization structure +- Emergency access procedures (non-SSO owner account) + +## User access management + +### IDP app assignment strategies + +#### Per-environment access control + +Control who can access each environment by managing app assignments in your IDP: + +``` +Engineering Team: +├─ Supabase Dev (assigned) ✅ +├─ Supabase Staging (assigned) ✅ +└─ Supabase Prod (assigned) ✅ + +QA Team: +├─ Supabase Dev (assigned) ✅ +├─ Supabase Staging (assigned) ✅ +└─ Supabase Prod (NOT assigned) ❌ + +Contractors: +├─ Supabase Dev (assigned) ✅ +├─ Supabase Staging (NOT assigned) ❌ +└─ Supabase Prod (NOT assigned) ❌ +``` + +### Role assignment patterns + +#### Option 1: Different default roles per environment + +- Dev: Auto-join with "Administrator" role (developers need full control) +- Staging: Auto-join with "Developer" role +- Prod: No auto-join, explicit invitations with "Read-only" or "Developer" + +#### Option 2: Consistent roles, manual promotion + +- All environments: Auto-join with "Developer" role +- Promote to "Administrator" or "Owner" manually as needed +- Provides consistent baseline, explicit elevation + +#### Option 3: No auto-join, explicit control + +- All environments: Auto-join disabled +- Send explicit invitations with appropriate roles +- Maximum control, more management overhead + +Choose based on your organization's security posture and operational preferences. + +## Best practices + +### Naming conventions + +#### IDP app names + +Use consistent, descriptive names that clearly indicate the environment: + +- ✅ "Supabase - Production" +- ✅ "Supabase Prod" +- ❌ "Supabase" (ambiguous) +- ❌ "SUPA_PROD" (unclear abbreviation) + +#### Supabase organization names + +Match your IDP app names when possible: + +- IDP app: "Supabase - Production" → Org: `acme-production` +- IDP app: "Supabase - Staging" → Org: `acme-staging` +- IDP app: "Supabase - Dev" → Org: `acme-development` + +### Configuration synchronization + +Keep critical settings synchronized across environments: + +- **Attribute mappings**: Should be identical across all providers +- **Certificate settings**: Coordinate renewals across all environments +- **Safety accounts**: Each org needs a non-SSO owner account + +#### Configuration drift checklist + +- [ ] Attribute mappings match across environments +- [ ] Certificate expiration dates documented for all providers +- [ ] Non-SSO owner accounts exist in all organizations +- [ ] Auto-join settings are intentional (not accidental) +- [ ] Default roles appropriate for each environment + +### Testing in lower environments first + +Always test SSO changes in non-production environments: + +1. **Make change in Dev environment** +2. **Test thoroughly** (see [testing guide](/docs/guides/platform/sso/testing-best-practices)) +3. **Deploy to Staging** and verify +4. **Monitor for issues** (1-2 days) +5. **Deploy to Production** during low-usage period +6. **Monitor closely** after production deployment + +### Security considerations + +#### Environment isolation + +- Never reuse metadata between environments (security risk) +- Each environment should have unique ACS URLs +- Verify IDP app assignments are correct (don't give prod access accidentally) + +#### Access reviews + +- Quarterly review of who has access to production +- Verify IDP app assignments are up to date +- Remove access for users who have changed roles +- Audit auto-join configurations (still appropriate?) + +#### Break-glass access + +Each organization must have at least one non-SSO owner account: + +- Create dedicated "break-glass" accounts +- Store credentials in secure password manager +- Test these accounts regularly (quarterly) +- Document emergency access procedures + +## Troubleshooting + +### Users accessing the wrong environment + +#### Symptom + +User clicks "Supabase Prod" tile but sees the dev environment. + +#### Causes + +- Metadata configured incorrectly (swapped between environments) +- ACS URL points to wrong organization +- User has bookmarked the wrong organization + +#### Solution + +1. Verify ACS URL in IDP app configuration +2. Compare metadata in Supabase SSO settings +3. Check that organization slug matches expected environment +4. Have user clear browser cookies and try again +5. Verify user is clicking correct app tile + +### Configuration drift between environments + +#### Symptom + +SSO works in dev but fails in staging or production. + +#### Causes + +- Attribute mappings differ between providers +- Certificate expired in one environment but not others +- Domain configuration inconsistent (if using SP-initiated) + +#### Solution + +1. Compare SSO configurations side-by-side +2. Check attribute mappings are identical +3. Verify certificate expiration dates +4. Test with same user account across all environments +5. Review IDP audit logs for authentication failures + +### Users don't see expected app tiles + +#### Symptom + +User cannot find "Supabase Staging" tile in IDP dashboard. + +#### Causes + +- User not assigned to the app in IDP +- App not deployed/published in IDP +- User looking in wrong place (different IDP portal) + +#### Solution + +1. Verify app assignment in IDP admin console +2. Check app is published/active +3. Confirm user has logged out and back into IDP +4. Verify user is checking correct IDP portal (some organizations have multiple) + +### Auto-join adding users to wrong organization + +#### Symptom + +User joins dev environment when they should join production. + +#### Cause + +User clicked wrong app tile, auto-join is enabled. + +#### Prevention + +- Disable auto-join in production (explicit invitations only) +- Use clear app tile naming +- Document which tile corresponds to which environment +- Consider using different IDP groups for different environments + +#### Remediation + +1. Remove user from incorrect organization +2. Send explicit invitation to correct organization +3. Educate user on correct app tile to use +4. Consider disabling auto-join to prevent recurrence + +### Multiple organizations with same domain (SP-initiated confusion) + +#### Symptom + +With SP-initiated enabled and same domain in multiple organizations, users get routed to unexpected organization. + +#### Cause + +SP-initiated routing uses first matching provider. + +#### Solution + +- **Recommended:** Switch to IDP-initiated only (disable SP-initiated) +- Remove domain configuration from all but one organization +- Provide clear IDP app tiles for explicit routing +- Document which organization users should access via SP-initiated + +## Migration from single to multiple providers + +If you currently have a single SSO provider and need to add more: + +### Phase 1: Planning + +1. Decide which pattern to use (environments, teams, etc.) +2. Document new organization structure +3. Identify users for each environment +4. Plan user communication strategy + +### Phase 2: Create new organizations + +1. Create additional Supabase organizations +2. Configure projects within each organization +3. Migrate data if needed (see [project transfer](/docs/guides/platform/project-transfer)) + +### Phase 3: Configure SSO providers + +1. Create additional SAML apps in your IDP +2. Configure SSO in each new organization +3. Start with auto-join disabled +4. Test with small group + +### Phase 4: Migrate users + +1. Communicate changes to users +2. Assign users to appropriate IDP apps +3. Test that users can access correct environments +4. Enable auto-join if desired + +### Phase 5: Decommission old configuration (if applicable) + +1. Migrate all users to new structure +2. Verify no one depends on old configuration +3. Disable old SSO provider +4. Monitor for issues +5. Delete after verification period + +## Next steps + +- **Configure your IDP:** Follow your [provider-specific guide](/docs/guides/platform/sso#supported-providers) +- **Test thoroughly:** Review [SSO Testing and Best Practices](/docs/guides/platform/sso/testing-best-practices) +- **Understand login flows:** Read [Understanding SSO Login Flows](/docs/guides/platform/sso/login-flows) +- **Choose the right flow:** See [Choosing the Right Login Flow](/docs/guides/platform/sso/choosing-login-flow) diff --git a/apps/docs/content/guides/platform/sso/okta.mdx b/apps/docs/content/guides/platform/sso/okta.mdx index 933dd1e848b..370b410872a 100644 --- a/apps/docs/content/guides/platform/sso/okta.mdx +++ b/apps/docs/content/guides/platform/sso/okta.mdx @@ -94,6 +94,12 @@ We do not permit use of public domains like `gmail.com`, `yahoo.com`. + + +Each SSO provider can be configured with different email domains. For multi-environment setups (Dev/Staging/Prod), we recommend using IdP-initiated flow with multiple SAML apps under the same domain rather than domain-based routing. For more details, see the [Multiple SSO Providers guide](/docs/guides/platform/sso/multiple-providers). + + + ## Step 9: Configure metadata [#dashboard-configure-metadata] Enter the metadata URL you obtained from [Step 6](#idp-metadata-url) into the Metadata URL field: @@ -114,11 +120,17 @@ If you did not customize your settings you may save some time by clicking the ** ## Step 11: Join organization on signup (optional) [#dashboard-configure-autojoin] + + +**Recommended workflow:** Start with auto-join **disabled** to test your SSO configuration. Once SSO login is working correctly, enable auto-join if desired. + + + By default this setting is disabled, users logging in via SSO will not be added to your organization automatically. ![Auto-join disabled](/docs/img/sso-dashboard-configure-autojoin-disabled.png) -Toggle this on if you want SSO-authenticated users to be **automatically added to your organization** when they log in via SSO. +Toggle this on if you want SSO-authenticated users to be **automatically added to your organization** when they log in via SSO. Auto-join applies on **every login**, not just first signup - this makes it safe to test SSO before enabling this feature. ![Auto-join enable](/docs/img/sso-dashboard-configure-autojoin-enabled.png) @@ -126,7 +138,7 @@ When auto-join is enabled, you can choose the **default role** for new users: ![Auto-join role selection](/docs/img/sso-dashboard-configure-autojoin-enabled-role.png) -Choose a role that fits the level of access you want to grant to new members. +We recommend choosing **Developer** as the default role (principle of least privilege) and promoting users individually as needed. @@ -134,10 +146,26 @@ Visit [access-control](/docs/guides/platform/access-control) documentation for d -## Step 12: Save changes and test single sign-on [#dashboard-configure-save] +## Step 12: Save changes [#dashboard-configure-save] When you click **Save changes**, your new SSO configuration is applied immediately. From that moment, any user with an email address matching one of your configured domains who visits your organization's sign-in URL will be routed through the SSO flow. -We recommend asking a few users to test signing in via their Okta account. They can do this by entering their email address on the [Sign in with SSO](/dashboard/sign-in-sso) page. + -If SSO sign-in doesn't work as expected, contact your Supabase support representative for assistance. +**Next step: Test your SSO configuration** + +Before rolling out SSO to your organization, we strongly recommend thorough testing. Visit our [SSO Testing and Best Practices](/docs/guides/platform/sso/testing-best-practices) guide for: + +- Step-by-step testing instructions +- How to verify auto-join works correctly +- Common issues and troubleshooting +- Security best practices +- Pre-launch checklist + + + + + +**Testing in Okta sandbox:** If your organization has an Okta sandbox environment, consider testing your SSO configuration there first before applying to production. + + diff --git a/apps/docs/content/guides/platform/sso/testing-best-practices.mdx b/apps/docs/content/guides/platform/sso/testing-best-practices.mdx new file mode 100644 index 00000000000..da7e08f9de7 --- /dev/null +++ b/apps/docs/content/guides/platform/sso/testing-best-practices.mdx @@ -0,0 +1,844 @@ +--- +title: 'SSO Testing and Best Practices' +description: 'Comprehensive guide to testing SSO configuration and best practices for secure, reliable single sign-on.' +--- + +After configuring your SSO provider, thorough testing is essential before rolling out to your organization. This guide covers testing procedures, troubleshooting common issues, and best practices for maintaining a secure SSO setup. + +## Pre-configuration checklist + +Before you begin testing, verify: + +- [ ] Organization has Team or Enterprise plan +- [ ] Login flow type decided (IdP-initiated, SP-initiated, or both) - see [Choosing a Login Flow](/docs/guides/platform/sso/choosing-login-flow) +- [ ] Email domains identified (only required if using SP-initiated) +- [ ] Auto-join settings and default role are decided +- [ ] **At least one non-SSO owner account exists** (critical safety requirement) +- [ ] Certificate expiration dates are documented (especially for Google Workspace) + +## Testing login flows + +Before testing auto-join and other features, verify which login flows work for your SSO configuration. See [Understanding SSO Login Flows](/docs/guides/platform/sso/login-flows) for technical details. + +### Testing IdP-initiated login + +IdP-initiated login is always available and doesn't require domain configuration. This should be your primary test. + +**Test procedure:** + +1. **Access from identity provider:** + - Open your IdP dashboard (Okta, Azure AD, Google Workspace) + - Locate your Supabase app tile or bookmark + - Click the app tile + +2. **Verify authentication:** + - If already authenticated with IdP: Immediate redirect to Supabase + - If not authenticated: Complete IdP login flow, then redirect + - Check you're logged into the correct organization + - Verify user profile information is populated correctly + +3. **Confirm success:** + - No error messages appear + - Organization dashboard loads properly + - User attributes (name, email) mapped correctly + +**Expected results:** + +- ✅ Direct login from IdP with no intermediate steps +- ✅ Works regardless of domain configuration +- ✅ User information properly mapped from IdP + +### Testing SP-initiated login + +SP-initiated login requires domain configuration. Only test this if you've enabled SP-initiated flow. + + + +If you haven't configured domains or have "Enable SP-initiated login" disabled, skip this test. SP-initiated will not work without domain configuration. + + + +**Prerequisites:** + +- "Enable SP-initiated login" toggle is **ON** +- At least one email domain configured + +**Test procedure:** + +1. **Start at Supabase:** + - Visit [Sign in with SSO](/dashboard/sign-in-sso) + - Or click "Sign in with SSO" from main sign-in page + +2. **Enter email:** + - Use email with matching configured domain + - Click "Continue" + +3. **Verify redirect chain:** + - Should redirect to your identity provider + - Complete authentication if needed + - Should redirect back to Supabase + - Check you're in correct organization + +4. **Test domain matching:** + - Try email with non-matching domain + - Should receive error: "No SSO provider found" + - Confirms domain-based routing works + +**Expected results:** + +- ✅ Users with matching domains redirected to IdP +- ✅ Non-matching domains show clear error message +- ✅ Successful authentication redirects back to Supabase + +### Testing without domains (IdP-initiated only) + +This is a critical test for domainless providers, which enable multiple SAML apps per domain. + +**Test scenario:** + +You've configured SSO with: + +- "Enable SP-initiated login" **OFF** (or no domains configured) +- IdP metadata configured +- Attribute mappings configured + +**Test procedure:** + +1. **Verify SP-initiated is unavailable:** + - Visit [Sign in with SSO](/dashboard/sign-in-sso) + - Enter your email address + - Expected: Error message "No SSO provider found" + - This is correct behavior (no domains = no SP-initiated) + +2. **Verify IdP-initiated works:** + - Open IdP dashboard + - Click Supabase app tile + - Should successfully log in + - Verify correct organization access + +3. **Confirm multi-environment pattern works (if applicable):** + - If you have multiple environments (Dev/Staging/Prod) + - Each should have separate app tile in IdP + - Click each tile individually + - Verify each routes to correct organization + +**Expected results:** + +- ✅ IdP-initiated login works perfectly +- ❌ SP-initiated login unavailable (expected) +- ✅ Multiple environments accessible via different tiles +- ✅ No domain conflicts between environments + + + +**Multiple environments:** If you're setting up Dev/Staging/Prod, this domainless pattern is recommended. See [Multiple SSO Providers](/docs/guides/platform/sso/multiple-providers) for detailed configuration guidance. + + + +### Login flow verification checklist + +- [ ] IdP-initiated login works from IdP dashboard +- [ ] SP-initiated login works (if domains configured) +- [ ] SP-initiated properly blocked if no domains configured +- [ ] Domain matching works correctly for SP-initiated +- [ ] Non-matching domains show appropriate errors +- [ ] Multiple environments route correctly (if using multiple providers) +- [ ] Both flows work simultaneously (if both enabled) + +## Testing SSO login flow + +### Basic login test + +1. **Navigate to the SSO sign-in page**: + - Visit [Sign in with SSO](/dashboard/sign-in-sso) + - Or click "Sign in with SSO" from the main Supabase sign-in page + +2. **Enter your email address**: + - Use an email with a domain configured in your SSO settings + - Click "Continue" + +3. **Verify redirect to identity provider**: + - You should be redirected to your identity provider (Okta, Azure AD, Google Workspace) + - If already signed in to your IdP, you may be automatically redirected back + - If not signed in, complete the IdP login flow + +4. **Confirm successful sign-in**: + - You should be redirected back to Supabase dashboard + - Your profile should show your SSO identity + - Check that your user information is populated correctly + +### Multi-user testing + +Test with 2-3 additional users to verify: + +- Users with matching email domains can sign in via SSO +- User attributes (name, email) are mapped correctly +- Users receive appropriate access to organization (if using auto-join) +- Users without matching domains cannot use SSO for this organization + +## Testing auto-join + + + +**Recent improvement:** Auto-join now applies on EVERY login, not just first signup. This resolves a common issue where org owners would test with auto-join disabled, enable it, then log in again expecting to auto-join. + + + +### Recommended testing workflow + +1. **Start with auto-join disabled**: + - Navigate to [SSO settings](/dashboard/org/_/sso) + - Ensure "Join organization on signup" is **disabled** + - Configure your SSO provider + - Test basic SSO login (see above) + +2. **Enable auto-join after successful test**: + - Return to [SSO settings](/dashboard/org/_/sso) + - Toggle "Join organization on signup" to **enabled** + - Select default role (recommended: **Developer**) + - Click "Save changes" + +3. **Test auto-join with your account**: + - **Log out completely** from Supabase + - Sign in again via SSO + - Verify you were automatically added to the organization + - Check you received the correct default role + +4. **Test with additional users**: + - Have colleagues with matching email domains sign in via SSO + - They should automatically join the organization + - Verify they received the correct default role + - Check organization members list at `/dashboard/org/_/team` + +5. **Test domain restrictions (if using SP-initiated)**: + - Try signing in with an email from a non-configured domain + - User should be able to sign in but will NOT see the organization + - This confirms domain-based access control is working + - Note: With IdP-initiated only, domain matching doesn't apply + +6. **Test idempotency (prevents duplicate memberships)**: + - Log in again with an account that's already a member + - Verify no error occurs + - Check members list - should be no duplicate entry + - Confirm role hasn't changed unexpectedly + - Expected: Auto-join gracefully handles existing members + +7. **Test with domainless (IdP-initiated only) configuration**: + + + + This test is critical if you're using multiple environments under the same domain. See [Multiple SSO Providers](/docs/guides/platform/sso/multiple-providers) for details. + + + +- If you configured SSO without domains (IdP-initiated only): + - Enable auto-join + - New user accesses via IdP app tile + - Verify auto-join works without domain check + - User automatically added to organization + - Correct role assigned + - Expected: Auto-join works for IdP-initiated regardless of email domain + +8. **Test auto-join re-enablement**: + - Disable auto-join + - Have new user sign in via SSO + - Verify they are NOT added to organization + - Re-enable auto-join + - Same user logs out and logs in again + - Verify they ARE now added to organization + - Expected: Existing SSO users auto-join when feature is enabled + +### Auto-join verification checklist + +- [ ] Auto-join works when enabled +- [ ] Users receive correct default role +- [ ] Non-matching domains are excluded (if using SP-initiated with domains) +- [ ] Existing users auto-join on their next login (not just new signups) +- [ ] Auto-join can be disabled and re-enabled as needed +- [ ] Auto-join is idempotent (no duplicate memberships) +- [ ] Auto-join works with IdP-initiated only (no domains) +- [ ] Auto-join works with both IdP and SP-initiated flows + +## Testing invitations + + + +**Recent improvement:** You can now explicitly choose whether an invitation requires SSO or non-SSO authentication. Previously, this was inherited from the inviter's account type, which caused confusion. + + + +### Creating and testing invitations + +1. **Create SSO-required invitation**: + - Navigate to [organization team settings](/dashboard/org/_/team) + - Click "Invite" to create a new invitation + - Select **"Require SSO"** option + - Enter recipient email and select role + - Send invitation + - Recipient must log in via SSO to accept + +2. **Create non-SSO invitation**: + - Create a new invitation + - Select **"Non-SSO"** option + - Send invitation + - Recipient can use password or social login to accept + +3. **Test SSO mismatch scenario**: + - Create an SSO-required invitation + - Have recipient try to accept while logged in with a non-SSO account + - Error should display: "Invite token SSO provider does not match the one you are logged in with" + - Recipient should log out and sign in via SSO + - Can then successfully accept the invitation + +### Common invitation scenarios + +- **All-SSO organization**: Always select "Require SSO" for invitations +- **Mixed organization**: Choose based on recipient's authentication method +- **Transitioning to SSO**: Start with non-SSO users, gradually add SSO users, maintain non-SSO owner before removing old authentication methods + +### Invitation verification checklist + +- [ ] SSO-required invitations work correctly +- [ ] Non-SSO invitations work correctly +- [ ] SSO mismatch error message is clear +- [ ] Mixed authentication organization functions properly +- [ ] Invitations can be resent if needed + + + +If you're configuring multiple SSO providers for different environments (dev/staging/prod), the testing steps outlined here apply to each provider individually. For advanced multi-provider configuration strategies, see the [Multiple SSO Providers guide](/docs/guides/platform/sso/multiple-providers). + + + +## Testing SSO account restrictions + +SSO accounts have specific restrictions to prevent accidental organization lockouts. + + + +**Safety mechanism:** SSO accounts cannot delete SSO providers. This prevents scenarios where an SSO user could accidentally lock out the entire organization by deleting the SSO provider they use to authenticate. + + + +### Testing SSO account deletion restrictions + +1. **Log in with SSO account:** + - Authenticate via SSO (IdP or SP-initiated) + - Navigate to [SSO settings](/dashboard/org/_/sso) + - Verify you are an organization owner + +2. **Attempt to delete SSO provider:** + - Try to delete the SSO provider + - **Expected:** Error message preventing deletion + - Error: "Only a non-SSO account may delete an SSO Provider" + - Deletion should be blocked + +3. **Verify other SSO operations work:** + - SSO accounts CAN read SSO configuration + - SSO accounts CAN update SSO settings + - SSO accounts CAN disable (but not delete) SSO provider + - Only deletion is restricted + +**Expected results:** + +- ❌ SSO accounts cannot delete SSO providers +- ✅ Clear error message explains the restriction +- ✅ Other SSO management operations still work + +### Testing with non-SSO owner account + +1. **Log in with non-SSO owner:** + - Use password or social auth account + - Must be organization owner + - Navigate to [SSO settings](/dashboard/org/_/sso) + +2. **Verify deletion capability:** + - Non-SSO owners CAN delete SSO providers + - Deletion subject to additional safety checks (see next section) + - System allows proceeding to deletion flow + +**Expected results:** + +- ✅ Non-SSO owners CAN access deletion functionality +- ✅ Safety checks still apply (non-SSO account requirement) + +### SSO account restrictions checklist + +- [ ] SSO accounts cannot delete SSO providers +- [ ] SSO accounts CAN update SSO settings +- [ ] SSO accounts CAN disable SSO providers +- [ ] Non-SSO owners CAN delete SSO providers +- [ ] Error messages clearly explain the restriction +- [ ] Restriction applies to all SSO accounts (not just certain roles) + +## Common issues and troubleshooting + +Based on customer pain points that previously required support intervention: + +### Critical issues + +#### "Enabled auto-join but users aren't automatically joining" + +**Common workflow that causes this:** + +1. Org owner tests SSO with auto-join disabled +2. Enables auto-join after testing +3. Logs in again expecting to auto-join but nothing happens + +**Solution:** + +- Auto-join now applies on **every login**, not just first signup +- To test: Enable auto-join, log out completely, log back in via SSO +- If still not working, verify domain configuration matches user email exactly + +#### "Can't invite users with the right authentication type" + +**Previous limitation:** + +- Invitation type was inherited from inviter's account type +- Non-SSO owners couldn't send SSO invitations +- SSO owners couldn't send non-SSO invitations + +**Solution:** + +- When creating invitations, explicitly choose "Require SSO" or "Non-SSO" +- Mixed organizations are now fully supported +- Both SSO and non-SSO users can coexist in the same organization + +#### "Invitation acceptance shows 'SSO provider mismatch' error" + +**Cause:** User is logged in with wrong authentication method for the invitation + +**Solution:** + +1. Check if invitation requires SSO or non-SSO login +2. Log out completely +3. Sign in with the correct method (SSO or password/social) +4. Accept the invitation +5. Contact the person who sent the invitation if unsure about the type + +#### "Deleted the SSO provider and now members can't log in" + +**Recent safety improvements:** + +- System now automatically removes all SSO members before deletion +- Must have at least one non-SSO owner before deletion is allowed + +**Best practice:** + +- Add a non-SSO owner account **before** deleting SSO provider +- Communicate to affected users before deletion +- Consider disabling rather than deleting if change is temporary + +## Testing safe provider deletion + + + +**Critical safety checks:** Deleting an SSO provider automatically removes ALL SSO members from the organization. The system enforces multiple safety checks to prevent complete organization lockout. + +**Only test deletion in non-production environments or test organizations.** Do not test this in your actual production organization unless you fully understand the consequences. + + + +### Understanding deletion behavior + +When an SSO provider is deleted: + +1. System verifies at least one non-SSO owner account exists +2. **All SSO members are automatically removed** from the organization +3. SSO provider configuration is deleted +4. Organization continues operating with remaining non-SSO members + +This behavior prevents "orphaned" SSO accounts that can no longer authenticate. + +### Test 1: Deletion without non-SSO accounts (should fail) + +**Setup:** + +- Organization with only SSO accounts +- All owners authenticate via SSO +- No password or social auth owners exist + +**Test procedure:** + +1. **Verify current state:** + - Check [team settings](/dashboard/org/_/team) + - Confirm all owners are SSO accounts + - No non-SSO owner exists + +2. **Attempt deletion:** + - Navigate to [SSO settings](/dashboard/org/_/sso) + - Try to delete SSO provider + - **Expected:** Error preventing deletion + - Error message: "At least one non-SSO account is required to maintain organization access" + +3. **Verify organization state:** + - SSO provider still exists + - All SSO members still have access + - No partial deletion occurred + +**Expected result:** ❌ Deletion blocked with clear error message + +### Test 2: Add non-SSO owner and retry deletion (should succeed) + + + +**Warning:** This test WILL remove all SSO members. Only perform in test organizations. + + + +**Setup:** + +1. **Add non-SSO owner:** + - Create or invite a non-SSO user (password or social login) + - Promote to owner role + - **Critical:** Verify non-SSO owner can log in BEFORE deletion + - Store credentials securely + +2. **Document SSO members:** + - Note current SSO member count + - Document SSO member email addresses (for verification) + +**Test procedure:** + +1. **Attempt deletion as non-SSO owner:** + - Log in with non-SSO owner account + - Navigate to [SSO settings](/dashboard/org/_/sso) + - Delete SSO provider + - Confirm deletion + +2. **Verify deletion results:** + - All SSO members removed from organization + - Check [team settings](/dashboard/org/_/team) + - Only non-SSO members should remain + - SSO configuration completely removed + +3. **Verify non-SSO access:** + - Non-SSO owner retains full access + - Organization remains functional + - Can invite new members (non-SSO invitations) + +**Expected result:** ✅ Deletion succeeds, SSO members removed, non-SSO access preserved + +### Test 3: Member removal during deletion + +**Test in isolated environment with test accounts.** + +**Setup:** + +1. Create test organization +2. Enable SSO +3. Add multiple SSO members (3-5 test users) +4. Add at least one non-SSO owner +5. Document member list before deletion + +**Test procedure:** + +1. **Track members before deletion:** + - Note SSO member count (e.g., 5 SSO members) + - Note SSO member email addresses + - Document their roles + +2. **Delete SSO provider:** + - Log in as non-SSO owner + - Delete SSO provider + - System may show member count being removed + +3. **Verify member removal:** + - Check organization members list + - All SSO members should be gone + - Only non-SSO members remain + - Previous SSO users cannot access org + +**Expected result:** All SSO members cleanly removed, no orphaned accounts + +### Test 4: Mixed authentication scenario + +**Setup:** + +- Organization with both SSO and non-SSO members +- SSO members: employees (via SSO) +- Non-SSO members: contractors (password auth) +- Mixed owner types + +**Test procedure:** + +1. **Ensure non-SSO owner exists:** + - Verify at least one non-SSO owner + - Test their login before deletion + +2. **Delete SSO provider:** + - System allows deletion (non-SSO owner exists) + - Confirm deletion + +3. **Verify selective removal:** + - SSO members removed (employees) + - Non-SSO members retained (contractors) + - Organization still functional + - Non-SSO owners can manage organization + +**Expected result:** ✅ Selective removal - only SSO members affected + +### Safe deletion verification checklist + +- [ ] Cannot delete without non-SSO owner account +- [ ] Error message clearly explains requirement +- [ ] Adding non-SSO owner enables deletion +- [ ] All SSO members removed upon deletion +- [ ] Non-SSO members unaffected by SSO provider deletion +- [ ] Organization remains accessible via non-SSO accounts +- [ ] SSO configuration completely removed after deletion +- [ ] Non-SSO owner account tested BEFORE deletion + +### Best practices for safe deletion + +**Before deleting SSO provider:** + +1. **Create dedicated non-SSO owner account** + - Use password authentication + - **Test that this account can log in** + - Store credentials in secure password manager + - Verify owner permissions + +2. **Communication plan:** + - Notify all SSO members before deletion + - Explain they will lose organization access + - Provide timeline for deletion + - Offer alternative access if needed (re-invite as non-SSO) + +3. **Documentation:** + - Document which members will be removed + - Plan for re-adding users if needed + - Have rollback plan (reconfigure SSO if needed) + +**After deletion:** + +1. **Verify organization function:** + - Test non-SSO owner access + - Verify critical functionality works + - Check that SSO members removed + +2. **User communication:** + - Confirm to users that deletion complete + - Provide instructions for alternative access + - Answer questions about regaining access + +### Configuration issues + +#### "SSO sign-in doesn't work at all" + +Common causes: + +- Metadata URL/file incorrect or expired +- Certificate expired (especially Google Workspace - check during setup) +- Attribute mapping misconfigured +- User not assigned to Supabase app in identity provider +- Email domain not configured in Supabase SSO settings +- User email domain doesn't match configured domains + +**Troubleshooting steps:** + +1. Verify metadata URL/file is accessible and current +2. Check certificate expiration date +3. Verify attribute mappings (email mapping is required) +4. Confirm user is assigned to app in IdP +5. Check domain configuration in Supabase matches user email +6. Review IdP logs for authentication errors + +#### "Attribute mapping errors or missing user data" + +**Requirements:** + +- Email mapping to `email` is **required** +- Attribute keys must be spelled exactly as shown in provider +- Use provider presets (Okta, Azure, G Suite) to avoid errors + +**Troubleshooting:** + +1. Verify email attribute is mapped correctly +2. Check attribute names match your IdP configuration exactly +3. Test that mappings return expected user data +4. Use IdP test tools to see what attributes are being sent + +#### "Cannot delete SSO provider" + +**Error:** "At least one non-SSO account is required" + +**Solution:** + +1. Create or convert an existing member to a non-SSO owner account +2. Verify the non-SSO owner can log in +3. Then proceed with SSO provider deletion + +**Why this is required:** Prevents complete organization lockout if SSO becomes unavailable + +## Best practices + +### Security and safety + +#### CRITICAL: Maintain at least one non-SSO owner account + +- **Required** to prevent complete organization lockout +- System enforces this when deleting SSO provider +- Create dedicated non-SSO owner **before** enabling SSO +- Store credentials securely in a password manager +- Verify this account can log in before critical changes + +#### Monitor certificate expiration + +- Set calendar reminders **30 days before** certificate expiration +- Especially important for Google Workspace (certificates shown during setup) +- Test SSO after certificate renewal +- Update metadata in Supabase after IdP certificate renewal +- Communicate planned renewal to team + +#### Configure domains carefully + +- Use specific corporate email domains only +- Public domains (gmail.com, yahoo.com, etc.) are automatically blocked +- Be cautious with domains you don't fully control +- Multiple domains supported for contractors/acquisitions +- Document which domains are configured and why + +#### Regular access reviews + +- Periodically review organization member list +- Verify auto-join role is still appropriate +- Check for orphaned or inactive accounts +- Coordinate with IT team on user access reviews +- Remove members who no longer need access + +### Configuration and testing + +#### Recommended SSO setup workflow + +1. Create or verify non-SSO owner account exists +2. Configure SSO provider with auto-join **DISABLED** +3. Test SSO login with your own account +4. Verify attribute mappings are correct +5. Test with 2-3 additional users +6. Enable auto-join if desired +7. Test auto-join functionality thoroughly +8. Communicate SSO availability to team + +#### Role selection for auto-join + +- Default to **"Developer"** role (principle of least privilege) +- Avoid "Owner" or "Administrator" for auto-join +- Promote users individually as needed +- Review and document your access control strategy +- See [access control documentation](/docs/guides/platform/access-control) for role details + +#### Attribute mapping + +- Email mapping is **REQUIRED** +- Use provider presets (Okta, Azure, G Suite) when available +- Document custom mappings for future reference +- Test mappings return expected user data +- Keep mappings consistent across environments + +#### Multi-environment strategy + +- Consider separate providers for dev/staging/prod +- Test configuration changes in non-production first +- Keep provider configurations synchronized +- Document differences between environments +- See [Multiple SSO Providers guide](/docs/guides/platform/sso/multiple-providers) for details + +### Operations and maintenance + +#### Before making SSO changes + +- Notify team members in advance +- Schedule during low-usage period if possible +- Have rollback plan ready +- Keep Supabase support contact information handy +- Document what you're changing and why + +#### After SSO configuration changes + +- Test login immediately +- Verify auto-join still works (if enabled) +- Check that invitations are working +- Confirm no users are locked out +- Monitor for support requests from team + +#### Before deleting SSO provider + +- Verify at least one non-SSO owner exists (system enforces) +- Understand that **all SSO members will be removed automatically** +- Communicate to affected users **before** deletion +- Consider disabling rather than deleting if temporary +- Have plan for users to regain access if needed + +#### Coordinating with IT/Security team + +- SSO changes may affect compliance requirements +- Certificate renewals require coordination +- User access reviews should include Supabase +- Incident response plans should consider SSO dependencies +- Document SSO configuration in your organization's runbook + +## Final verification checklist + +Before rolling out SSO to your organization: + +**Authentication & Login Flows:** + +- [ ] IdP-initiated login works from IdP dashboard +- [ ] SP-initiated login works (if domains configured) +- [ ] Appropriate login flow chosen for your use case +- [ ] Domain configuration correct (or intentionally empty for IdP-only) +- [ ] Multiple environments route correctly (if using multiple providers) + +**Auto-Join Functionality:** + +- [ ] Auto-join adds users to correct organization (if enabled) +- [ ] Auto-joined users receive correct default role +- [ ] Auto-join works on first login (not just signup) +- [ ] Existing users auto-join when feature enabled +- [ ] Auto-join is idempotent (no duplicate memberships) +- [ ] Auto-join works with IdP-initiated (no domains required) +- [ ] Non-matching domains excluded (if using SP-initiated) + +**Invitations:** + +- [ ] SSO-required invitations work correctly +- [ ] Non-SSO invitations work correctly +- [ ] Invitation types can be explicitly chosen +- [ ] SSO mismatch errors are clear + +**Safety & Access Controls:** + +- [ ] At least one non-SSO owner account exists +- [ ] Non-SSO owner account can log in successfully +- [ ] Non-SSO credentials stored securely +- [ ] SSO account deletion restrictions understood and tested +- [ ] Safe deletion behavior verified (if tested) + +**Configuration:** + +- [ ] Certificate expiration date documented with calendar reminders +- [ ] Team notified of SSO availability +- [ ] Login instructions provided (IdP tile and/or supabase.com) +- [ ] Rollback plan documented +- [ ] Support contact information available + +**Testing Completed:** + +- [ ] Tested with multiple user accounts +- [ ] Both login flows tested (if both enabled) +- [ ] Auto-join behavior verified +- [ ] SSO account restrictions confirmed +- [ ] Domain restrictions validated (if applicable) +- [ ] Multi-environment isolation verified (if using multiple providers) + +## Need help? + +If you encounter issues not covered in this guide, contact your Supabase support representative for assistance. When reaching out, include: + +- Organization name and URL +- SSO provider type (Okta, Azure AD, Google Workspace, etc.) +- Specific error messages +- Steps you've already tried +- Whether the issue affects all users or specific individuals diff --git a/apps/docs/content/guides/security/product-security.mdx b/apps/docs/content/guides/security/product-security.mdx index f400d93829e..2acf856faa8 100644 --- a/apps/docs/content/guides/security/product-security.mdx +++ b/apps/docs/content/guides/security/product-security.mdx @@ -19,7 +19,7 @@ Various products at Supabase have their own hardening and configuration guides, - [Row Level Security](/docs/guides/database/postgres/row-level-security) - [Column Level Security](/docs/guides/database/postgres/column-level-security) -- [Hardening the Data API](/docs/guides/api/hardening-data-api) +- [Data API](/docs/guides/database/data-api) - [Additional security controls for the Data API](/docs/guides/api/securing-your-api) - [Custom claims and role based access control](/docs/guides/api/custom-claims-and-role-based-access-control-rbac) - [Managing Postgres roles](/docs/guides/database/postgres/roles) diff --git a/apps/docs/content/guides/self-hosting.mdx b/apps/docs/content/guides/self-hosting.mdx index 2a4e94bfb1d..9f16ea9ba99 100644 --- a/apps/docs/content/guides/self-hosting.mdx +++ b/apps/docs/content/guides/self-hosting.mdx @@ -7,7 +7,7 @@ hideToc: true ## Get started -The fastest and recommended way to self-host Supabase is using Docker. +The fastest and recommended way to self-host Supabase is to use Docker.
@@ -55,7 +55,7 @@ There are several other options to deploy Supabase. If you're interested in help ## About self-hosting -Self-hosting is a good fit if you need full control over your data, have compliance requirements that prevent using managed services, or want to run Supabase in an isolated environment. +Self-hosting is a good fit if you need full control over your data, have compliance requirements that prevent you from using managed services, or want to run Supabase in an isolated environment. ### How self-hosted Supabase differs @@ -64,11 +64,7 @@ Self-hosted Supabase is different from: - **Supabase CLI** (local development), which is intended for development and testing only. - **Managed Supabase** platform, which is fully hosted and operated by Supabase. -### Telemetry - -Self-hosted Supabase (Docker) does not phone home or collect any telemetry. - -The **Supabase CLI** is a [separate tool](/docs/guides/local-development/cli/getting-started) from self-hosted Supabase and collects usage telemetry to help improve the developer experience. You can opt out by running `supabase telemetry disable` or setting `SUPABASE_TELEMETRY_DISABLED=1`. See [CLI telemetry](/docs/guides/local-development/cli/getting-started#telemetry) for other opt-out methods. +Self-hosted Supabase mimics a single project. Studio doesn't support multiple organizations or projects. Platform-only [features](/features) such as branching, advanced metrics beyond logs, managed backups and PITR, analytics and vector buckets, ETL, and the platform management API are **unavailable** in self-hosted configuration. Most settings are configured through [environment variables](https://github.com/supabase/supabase/blob/master/docker/.env.example). ### Your responsibilities when self-hosting @@ -76,10 +72,18 @@ When you self-host, **you are responsible for**: - Server provisioning and maintenance - Security hardening and keeping OS and services updated -- Maintaining the Postgres database +- Service configuration and management +- Postgres database maintenance +- High availability and scalability - Backups and disaster recovery - Monitoring and uptime +### Telemetry + +Self-hosted Supabase (run via Docker Compose) **does not phone home or collect any telemetry**. + +The **Supabase CLI** is a [separate tool](/docs/guides/local-development/cli/getting-started) and collects usage telemetry to help improve the developer experience. See [CLI telemetry](/docs/guides/local-development/cli/getting-started#telemetry) for opt-out methods. + ## Support and community Self-hosted Supabase is community-supported. diff --git a/apps/docs/content/guides/self-hosting/copy-from-platform-s3.mdx b/apps/docs/content/guides/self-hosting/copy-from-platform-s3.mdx index 1665b9aadc6..57c780c2bb6 100644 --- a/apps/docs/content/guides/self-hosting/copy-from-platform-s3.mdx +++ b/apps/docs/content/guides/self-hosting/copy-from-platform-s3.mdx @@ -39,7 +39,7 @@ For better performance with large files, use the direct storage hostname: `https ## Step 2: Create buckets on self-hosted -Buckets must exist on the destination before you can copy objects into them. You can create them through dashboard UI, or with **SQL Editor**. +Buckets must exist on the destination before you can copy objects into them. You can create them through the dashboard UI, or with the **SQL Editor**. @@ -93,7 +93,7 @@ Replace the credentials with your actual values. For self-hosted, use the `REGIO Verify both remotes connect: -```bash +```sh rclone lsd platform: rclone lsd self-hosted: ``` @@ -104,13 +104,13 @@ Both commands should list your buckets. Copy a single bucket: -```bash +```sh rclone copy platform:your-storage-bucket self-hosted:your-storage-bucket --progress ``` To copy all buckets: -```bash +```sh for bucket in $(rclone lsf platform: | tr -d '/'); do echo "Copying bucket: $bucket" rclone copy "platform:$bucket" "self-hosted:$bucket" --progress @@ -127,7 +127,7 @@ For large migrations, consider adding `--transfers 4` to increase parallelism, o Compare object counts between source and destination: -```bash +```sh rclone size platform:your-storage-bucket && \ rclone size self-hosted:your-storage-bucket ``` @@ -154,7 +154,7 @@ If rclone reports that a bucket doesn't exist on the self-hosted side, create it {/* supa-mdx-lint-disable-next-line Rule003Spelling */} For very large files, increase rclone's timeout: -```bash +```sh rclone copy platform:your-storage-bucket self-hosted:your-storage-bucket --timeout 30m ``` diff --git a/apps/docs/content/guides/self-hosting/custom-email-templates.mdx b/apps/docs/content/guides/self-hosting/custom-email-templates.mdx index 9135f12b86e..777933cf4a1 100644 --- a/apps/docs/content/guides/self-hosting/custom-email-templates.mdx +++ b/apps/docs/content/guides/self-hosting/custom-email-templates.mdx @@ -69,7 +69,7 @@ volumes/ Update the `auth` service to depend on `templates-server`, and pass the email template environment variables. Then add a `templates-server` service to serve the templates from `./volumes/templates`. -```yml +```yml name=docker-compose.yml services: auth: depends_on: @@ -90,7 +90,7 @@ services: #### What this configuration does -- Adds a `templates-server`service that runs alongside the Supabase services in the same docker network. +- Adds a `templates-server` service that runs alongside the Supabase services in the same docker network. - Serves your custom email template files from the `./volumes/templates` directory. - Keeps the templates-server private to the Docker network (no published ports), so it is not accessible from outside. - Allows the `auth` service to fetch templates via `http://templates-server/