mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 09:55:06 +03:00
Merge pull request #3134 from supabase/docs/plugins
Docs: improve the auth section
This commit is contained in:
17 files changed
+603
-333
No files matched your search
+4
-320
@@ -7,58 +7,7 @@ description: Use Supabase to Authenticate and Authorize your users.
|
||||
import Link from '@docusaurus/Link'
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
const providers = [
|
||||
{
|
||||
name: 'Apple',
|
||||
// logo: '/img/libraries/dart-icon.svg',
|
||||
href: '/docs/guides/auth/auth-apple',
|
||||
},
|
||||
{
|
||||
name: 'Bitbucket',
|
||||
// logo: '/img/libraries/dart-icon.svg',
|
||||
href: '/docs/guides/auth/auth-bitbucket',
|
||||
},
|
||||
{
|
||||
name: 'Discord',
|
||||
// logo: '/img/libraries/dart-icon.svg',
|
||||
href: '/docs/guides/auth/auth-discord',
|
||||
},
|
||||
{
|
||||
name: 'Facebook',
|
||||
// logo: '/img/libraries/dart-icon.svg',
|
||||
href: '/docs/guides/auth/auth-facebook',
|
||||
},
|
||||
{
|
||||
name: 'GitHub',
|
||||
// logo: '/img/libraries/dart-icon.svg',
|
||||
href: '/docs/guides/auth/auth-github',
|
||||
},
|
||||
{
|
||||
name: 'GitLab',
|
||||
// logo: '/img/libraries/dart-icon.svg',
|
||||
href: '/docs/guides/auth/auth-gitlab',
|
||||
},
|
||||
{
|
||||
name: 'Google',
|
||||
// logo: '/img/libraries/dart-icon.svg',
|
||||
href: '/docs/guides/auth/auth-google',
|
||||
},
|
||||
{
|
||||
name: 'Twitter',
|
||||
// logo: '/img/libraries/dart-icon.svg',
|
||||
href: '/docs/guides/auth/auth-twitter',
|
||||
},
|
||||
{
|
||||
name: 'Twitch',
|
||||
// logo: '/img/libraries/dart-icon.svg',
|
||||
href: '/docs/guides/auth/auth-twitch',
|
||||
},
|
||||
{
|
||||
name: 'Twilio',
|
||||
// logo: '/img/libraries/dart-icon.svg',
|
||||
href: '/docs/guides/auth/auth-twilio',
|
||||
},
|
||||
]
|
||||
import providers from '@site/src/data/authProviders'
|
||||
|
||||
## User Management
|
||||
|
||||
@@ -134,22 +83,6 @@ let user = await supabase.from('users').select('user_id, name')
|
||||
// Still => { id: 'd0714948', name: 'Jane' }
|
||||
```
|
||||
|
||||
## Policies Are Like `WHERE` Clauses
|
||||
|
||||
Policies are easy to understand once you get the hang of them. You can just think of them as adding a `WHERE` clause to every query. For example if you had a policy like this:
|
||||
|
||||
```sql
|
||||
create policy "Individuals can view their own todos." on todos for
|
||||
select using (auth.uid() = user_id);
|
||||
```
|
||||
|
||||
It would translate to this whenever a user tries to select from the todos table:
|
||||
|
||||
```sql
|
||||
select *
|
||||
from todos
|
||||
where auth.uid() = todos.user_id; -- Policy is implicitly added.
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
@@ -161,258 +94,9 @@ where auth.uid() = todos.user_id; -- Policy is implicitly added.
|
||||
|
||||
Supabase provides a special function in Postgres, `auth.uid()`, which extracts the user's UID from the JWT. This is especially useful when creating policies.
|
||||
|
||||
## Policy Examples
|
||||
|
||||
Here are some examples to show you the power of PostgreSQL's RLS. Each policy is attached to a table, and the policy is executed
|
||||
every time a table is accessed.
|
||||
|
||||
### Allow read access
|
||||
|
||||
```sql
|
||||
-- 1. Create table
|
||||
create table profiles (
|
||||
id uuid references auth.users,
|
||||
avatar_url text
|
||||
);
|
||||
|
||||
-- 2. Enable RLS
|
||||
alter table profiles
|
||||
enable row level security;
|
||||
|
||||
-- 3. Create Policy
|
||||
create policy "Public profiles are viewable by everyone."
|
||||
on profiles for select using (
|
||||
true
|
||||
);
|
||||
```
|
||||
|
||||
1. Creates a table called `profiles` in the public schema (default schema).
|
||||
2. Enables Row Level Security.
|
||||
3. Creates a policy which allows all `select` queries to run.
|
||||
|
||||
### Restrict updates
|
||||
|
||||
```sql
|
||||
-- 1. Create table
|
||||
create table profiles (
|
||||
id uuid references auth.users,
|
||||
avatar_url text
|
||||
);
|
||||
|
||||
-- 2. Enable RLS
|
||||
alter table profiles
|
||||
enable row level security;
|
||||
|
||||
-- 3. Create Policy
|
||||
create policy "Users can update their own profiles."
|
||||
on profiles for update using (
|
||||
auth.uid() = id
|
||||
);
|
||||
```
|
||||
|
||||
1. Creates a table called `profiles` in the public schema (default schema).
|
||||
2. Enables RLS.
|
||||
3. Creates a policy which allows logged in users to update their own data.
|
||||
|
||||
### Policies with joins
|
||||
|
||||
Policies can even include table joins. This example shows how you can query "external" tables to build more advanced rules.
|
||||
|
||||
```sql
|
||||
create table teams (
|
||||
id serial primary key,
|
||||
name text
|
||||
);
|
||||
|
||||
-- 2. Create many to many join
|
||||
create table members (
|
||||
team_id bigint references teams,
|
||||
user_id uuid references auth.users
|
||||
);
|
||||
|
||||
-- 3. Enable RLS
|
||||
alter table teams
|
||||
enable row level security;
|
||||
|
||||
-- 4. Create Policy
|
||||
create policy "Team members can update team details if they belong to the team."
|
||||
on teams
|
||||
for update using (
|
||||
auth.uid() in (
|
||||
select user_id from members
|
||||
where team_id = id
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
### Policies with security definer functions
|
||||
|
||||
Policies can also make use of `security definer functions`. This is useful in a many-to-many relationship where you want to restrict access to the linking table. Following the `teams` and `members` example from above, this example shows how you can use security definer function in combination with a policy to control access to the `members` table.
|
||||
|
||||
```sql
|
||||
-- 1. Follow example for 'Policies with joins' above
|
||||
|
||||
-- 2. Enable RLS
|
||||
alter table members
|
||||
enable row level security
|
||||
|
||||
-- 3. Create security definer function
|
||||
create or replace function get_teams_for_authenticated_user()
|
||||
returns setof bigint
|
||||
language sql
|
||||
security definer
|
||||
set search_path = public
|
||||
stable
|
||||
as $$
|
||||
select team_id
|
||||
from members
|
||||
where user_id = auth.uid()
|
||||
$$;
|
||||
|
||||
-- 4. Create Policy
|
||||
create policy "Team members can update team members if they belong to the team."
|
||||
on members
|
||||
for all using (
|
||||
team_id in (
|
||||
select get_teams_for_authenticated_user()
|
||||
)
|
||||
);
|
||||
|
||||
```
|
||||
|
||||
### Verifying email domains
|
||||
|
||||
Postgres has a function `right(string, n)` that returns the rightmost n characters of a string.
|
||||
You could use this to match staff member's email domains.
|
||||
|
||||
```sql
|
||||
-- 1. Create table
|
||||
create table leaderboard (
|
||||
id uuid references auth.users,
|
||||
high_score bigint
|
||||
);
|
||||
|
||||
-- 2. Enable RLS
|
||||
alter table leaderboard
|
||||
enable row level security;
|
||||
|
||||
-- 3. Create Policy
|
||||
create policy "Only Blizzard staff can update leaderboard"
|
||||
on leaderboard
|
||||
for update using (
|
||||
right(auth.email(), 13) = '@blizzard.com'
|
||||
);
|
||||
```
|
||||
|
||||
### Time to live for rows
|
||||
|
||||
Policies can also be used to implement TTL or time to live feature that you see in Instagram stories or Snapchat.
|
||||
In the following example, rows of `stories` table are available only if they have been created within the last 24 hours.
|
||||
|
||||
```sql
|
||||
-- 1. Create table
|
||||
create table if not exists stories (
|
||||
id uuid not null primary key DEFAULT uuid_generate_v4(),
|
||||
created_at timestamp with time zone default timezone('utc' :: text, now()) not null,
|
||||
content text not null
|
||||
);
|
||||
|
||||
-- 2. Enable RLS
|
||||
alter table stories
|
||||
enable row level security;
|
||||
|
||||
-- 3. Create Policy
|
||||
create policy "Stories are live for a day"
|
||||
on stories
|
||||
for select using (
|
||||
created_at > (current_timestamp - interval '1 day')
|
||||
);
|
||||
```
|
||||
|
||||
### Advanced policies
|
||||
|
||||
Use the full power of SQL to build extremely advanced rules.
|
||||
|
||||
In this example, we will create a `posts` and `comments` tables and then create a policy that depends on another policy.
|
||||
(In this case, the comments policy depends on the posts policy.)
|
||||
|
||||
```sql
|
||||
create table posts (
|
||||
id serial primary key,
|
||||
creator_id uuid not null references auth.users(id),
|
||||
title text not null,
|
||||
body text not null,
|
||||
publish_date date not null default now(),
|
||||
audience uuid[] null -- many to many table omitted for brevity
|
||||
);
|
||||
|
||||
create table comments (
|
||||
id serial primary key,
|
||||
post_id int not null references posts(id) on delete cascade,
|
||||
user_id uuid not null references auth.users(id),
|
||||
body text not null,
|
||||
comment_date date not null default now()
|
||||
);
|
||||
|
||||
create policy "Creator can see their own posts"
|
||||
on posts
|
||||
for select
|
||||
using (
|
||||
auth.uid() = posts.creator_id
|
||||
);
|
||||
|
||||
create policy "Logged in users can see the posts if they belong to the post 'audience'."
|
||||
on posts
|
||||
for select
|
||||
using (
|
||||
auth.uid() = any (posts.audience)
|
||||
);
|
||||
|
||||
create policy "Users can see all comments for posts they have access to."
|
||||
on comments
|
||||
for select
|
||||
using (
|
||||
exists (
|
||||
select 1 from posts
|
||||
where posts.id = comments.post_id
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
## Tips
|
||||
|
||||
### You don't have to use policies
|
||||
|
||||
You can also put your authorization rules in your middleware, similar to how you would create security rules with any other `backend <-> middleware <-> frontend` architecture.
|
||||
|
||||
Policies are a tool. In the case of "serverless/Jamstack" setups, they are especially effective because you don't have to deploy any middleware at all.
|
||||
|
||||
However, if you want to use another authorization method for your applications, that's also fine. Supabase is "just Postgres", so if your application
|
||||
works with Postgres, then it also works with Supabase.
|
||||
|
||||
Tip: Make sure to enable RLS for all your tables, so that your tables are inaccessible. Then use the "Service" which we provide, which is designed to bypass RLS.
|
||||
|
||||
### Check authentication settings on Supabase
|
||||
|
||||
Navigate to Authentication > Settings on [app.supabase.io](https://app.supabase.io), and you'll be able to change settings for things like:
|
||||
|
||||
- SITE URL, which is used for determining where to redirect users after they confirm their email addresses or attempt to use a magic link to log in.
|
||||
- Disabling email confirmations.
|
||||
- Enabling external OAuth providers, such as Google and GitHub.
|
||||
|
||||
### Never use a service key on the client
|
||||
|
||||
Supabase provides special "Service" keys, which can be used to bypass all RLS.
|
||||
These should never be used in the browser or exposed to customers, but they are useful for administrative tasks.
|
||||
|
||||
### Managing User Data
|
||||
|
||||
For security purposes, the `auth` schema is not exposed on the auto-generated API.
|
||||
Even though Supabase provides an `auth.users` table, it can be helpful to create tables in the `public` schema for storing user data.
|
||||
|
||||
You can read more about this in the [Managing User Data](/docs/guides/auth/managing-user-data) guide.
|
||||
|
||||
### Disable realtime for private tables
|
||||
#### Disable realtime for private tables
|
||||
|
||||
Our realtime server doesn't provide per-user security. Until we build a more robust auth system for WebSockets, you can disable realtime functionality for any private tables. To do this, you can manage the underlying Postgres replication publication:
|
||||
|
||||
@@ -437,9 +121,9 @@ alter publication supabase_realtime add table products;
|
||||
alter publication supabase_realtime add table posts;
|
||||
```
|
||||
|
||||
We're in the process of building enhanced realtime security.
|
||||
We're in the process of building [enhanced realtime security](https://github.com/supabase/walrus).
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Read more about [Row Level Security](https://www.postgresql.org/docs/current/ddl-rowsecurity.html).
|
||||
- Read more about Auth in the [Guides](/docs/guides/auth/intro).
|
||||
- Sign in: [app.supabase.io](https://app.supabase.io)
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: auth-apple
|
||||
title: "OAuth with Apple"
|
||||
title: "Login with Apple"
|
||||
description: Add Apple OAuth to your Supabase project
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: auth-bitbucket
|
||||
title: "OAuth with Bitbucket"
|
||||
title: "Login with Bitbucket"
|
||||
description: Add Bitbucket OAuth to your Supabase project
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: auth-discord
|
||||
title: "OAuth with Discord"
|
||||
title: "Login with Discord"
|
||||
description: Add Discord OAuth to your Supabase project
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: auth-facebook
|
||||
title: "OAuth with Facebook"
|
||||
title: "Login with Facebook"
|
||||
description: Add Facebook OAuth to your Supabase project
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: auth-github
|
||||
title: "OAuth with GitHub"
|
||||
title: "Login with GitHub"
|
||||
description: Add GitHub OAuth to your Supabase project
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: auth-gitlab
|
||||
title: "OAuth with GitLab"
|
||||
title: "Login with GitLab"
|
||||
description: Add GitLab OAuth to your Supabase project
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: auth-google
|
||||
title: "OAuth with Google"
|
||||
title: "Login with Google"
|
||||
description: Add Google OAuth to your Supabase project
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: auth-twilio
|
||||
title: SMS OTP with Twilio
|
||||
title: Phone Auth with Twilio
|
||||
description: How to set up and use Mobile OTP with Twilio and Supabase.
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: auth-twitch
|
||||
title: 'OAuth with Twitch'
|
||||
title: 'Login with Twitch'
|
||||
description: Add Twitch OAuth to your Supabase project
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: auth-twitter
|
||||
title: "OAuth with Twitter"
|
||||
title: "Login with Twitter"
|
||||
description: Add Twitter OAuth to your Supabase project
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
---
|
||||
id: intro
|
||||
title: Supabase Auth
|
||||
sidebar_label: Introduction
|
||||
description: Use Supabase to Authenticate and Authorize your users.
|
||||
# hide_table_of_contents: true
|
||||
---
|
||||
|
||||
import Link from '@docusaurus/Link'
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
import providers from '@site/src/data/authProviders'
|
||||
|
||||
Supabase Auth is designed to work with Postgres. There are two parts to every Auth system:
|
||||
|
||||
- Authentication: should this person be allowed in? If yes, who are they?
|
||||
- Authorization: once they are in, what are they allowed to do?
|
||||
|
||||
## Authentication
|
||||
|
||||
You can authenticate your users in several ways:
|
||||
|
||||
- Email & password.
|
||||
- Magic links (one-click logins).
|
||||
- Social providers.
|
||||
- Phone logins.
|
||||
|
||||
<div class="container" style={{ padding: 0 }}>
|
||||
<div class="row is-multiline">
|
||||
{providers.map((x) => (
|
||||
<div key={x.name} class="col col--4">
|
||||
<Link class="card" to={x.href}>
|
||||
<div class="card__body">
|
||||
<div class="" style={{ display: 'flex', justifyContent: 'space-between', gap: 10 }}>
|
||||
{x.logo && <img src={x.logo} alt={x.name} width="20" />}
|
||||
<p>{x.name}</p>
|
||||
<p>
|
||||
{x.official ? <span class={`badge badge--official`}>
|
||||
Official
|
||||
</span>:
|
||||
<span class={`badge badge--unofficial`}>
|
||||
Unofficial
|
||||
</span>
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 5 }}>
|
||||
<div class="code-block" style={{ width: '100%', display: 'flex', justifyContent: 'space-between', fontSize: '0.7rem' }}>
|
||||
<span>Platform:</span>
|
||||
<span>{x.platform.toString()}</span>
|
||||
</div>
|
||||
<div class="code-block" style={{ width: '100%', display: 'flex', justifyContent: 'space-between', fontSize: '0.7rem' }}>
|
||||
<span>Hosted:</span>
|
||||
<span>{x.selfHosted.toString()}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
## Authorization
|
||||
|
||||
|
||||
When you need granular authorization rules, nothing beats PostgreSQL's Row Level Security (RLS).
|
||||
|
||||
Policies are PostgreSQL's rule engine. They are incredibly powerful and flexible, allowing you to write complex SQL rules which fit your unique business needs.
|
||||
|
||||
Get started with our [Row Level Security Guides](/docs/guides/auth/row-level-security).
|
||||
@@ -76,4 +76,35 @@ const { data: profile } = await supabase
|
||||
|
||||
If you need to fetch a full list of user profiles, we supply a `service_key` which you can use with your API and Client Libraries to bypass Row Level Security.
|
||||
|
||||
Make sure you _NEVER_ expose this publicly. But it can be used on the server-side to fetch all of the profiles.
|
||||
Make sure you _NEVER_ expose this publicly. But it can be used on the server-side to fetch all of the profiles.
|
||||
|
||||
|
||||
## Advanced techniques
|
||||
|
||||
### Using triggers
|
||||
|
||||
If you want to add a row to your `public.users` table every time a user signs up, you can use triggers.
|
||||
If the trigger fails however, it could block the user sign ups - so make sure that the code is well-tested.
|
||||
|
||||
For example:
|
||||
|
||||
```sql
|
||||
-- inserts a row into public.users
|
||||
create function public.handle_new_user()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
security definer
|
||||
search_path = public
|
||||
as $$
|
||||
begin
|
||||
insert into public.users (id)
|
||||
values (new.id);
|
||||
return new;
|
||||
end;
|
||||
$$;
|
||||
|
||||
-- trigger the function every time a user is created
|
||||
create trigger on_auth_user_created
|
||||
after insert on auth.users
|
||||
for each row execute procedure public.handle_new_user();
|
||||
```
|
||||
@@ -0,0 +1,313 @@
|
||||
---
|
||||
id: row-level-security
|
||||
title: Row Level Security
|
||||
description: Secure your data using Postgres Row Level Security.
|
||||
---
|
||||
|
||||
When you need granular authorization rules, nothing beats PostgreSQL's [Row Level Security (RLS)](https://www.postgresql.org/docs/current/ddl-rowsecurity.html).
|
||||
|
||||
[Policies](https://www.postgresql.org/docs/current/sql-createpolicy.html) are PostgreSQL's rule engine. They are incredibly powerful and flexible, allowing you to write complex SQL rules which fit your unique business needs.
|
||||
|
||||
<iframe className="w-full video-with-border" width="640" height="385" src="https://www.youtube-nocookie.com/embed/Ow_Uzedfohk" frameBorder="1" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen></iframe>
|
||||
|
||||
|
||||
## Policies
|
||||
|
||||
Policies are easy to understand once you get the hang of them. Each policy is attached to a table, and the policy is executed
|
||||
every time a table is accessed.
|
||||
You can just think of them as adding a `WHERE` clause to every query. For example a policy like this ...
|
||||
|
||||
```sql
|
||||
create policy "Individuals can view their own todos."
|
||||
on todos for select
|
||||
using ( auth.uid() = user_id );
|
||||
```
|
||||
|
||||
.. would translate to this whenever a user tries to select from the todos table:
|
||||
|
||||
```sql
|
||||
select *
|
||||
from todos
|
||||
where auth.uid() = todos.user_id; -- Policy is implicitly added.
|
||||
```
|
||||
|
||||
## Helper Functions
|
||||
|
||||
Supabase provides you with a few easy functions that you can use with your policies.
|
||||
|
||||
|
||||
### `auth.uid()`
|
||||
|
||||
Returns the ID of the user making the request.
|
||||
|
||||
### `auth.role()`
|
||||
|
||||
Returns the role of the user making the request. In most cases this is either `authenticated` or `anon`.
|
||||
|
||||
### `auth.email()`
|
||||
|
||||
Retuns the email of the user making the request.
|
||||
|
||||
## Examples
|
||||
|
||||
Here are some examples to show you the power of PostgreSQL's RLS.
|
||||
|
||||
### Allow read access
|
||||
|
||||
```sql
|
||||
-- 1. Create table
|
||||
create table profiles (
|
||||
id uuid references auth.users,
|
||||
avatar_url text
|
||||
);
|
||||
|
||||
-- 2. Enable RLS
|
||||
alter table profiles
|
||||
enable row level security;
|
||||
|
||||
-- 3. Create Policy
|
||||
create policy "Public profiles are viewable by everyone."
|
||||
on profiles for select using (
|
||||
true
|
||||
);
|
||||
```
|
||||
|
||||
1. Creates a table called `profiles` in the public schema (default schema).
|
||||
2. Enables Row Level Security.
|
||||
3. Creates a policy which allows all `select` queries to run.
|
||||
|
||||
### Restrict updates
|
||||
|
||||
```sql
|
||||
-- 1. Create table
|
||||
create table profiles (
|
||||
id uuid references auth.users,
|
||||
avatar_url text
|
||||
);
|
||||
|
||||
-- 2. Enable RLS
|
||||
alter table profiles
|
||||
enable row level security;
|
||||
|
||||
-- 3. Create Policy
|
||||
create policy "Users can update their own profiles."
|
||||
on profiles for update using (
|
||||
auth.uid() = id
|
||||
);
|
||||
```
|
||||
|
||||
1. Creates a table called `profiles` in the public schema (default schema).
|
||||
2. Enables RLS.
|
||||
3. Creates a policy which allows logged in users to update their own data.
|
||||
|
||||
### Policies with joins
|
||||
|
||||
Policies can even include table joins. This example shows how you can query "external" tables to build more advanced rules.
|
||||
|
||||
```sql
|
||||
create table teams (
|
||||
id serial primary key,
|
||||
name text
|
||||
);
|
||||
|
||||
-- 2. Create many to many join
|
||||
create table members (
|
||||
team_id bigint references teams,
|
||||
user_id uuid references auth.users
|
||||
);
|
||||
|
||||
-- 3. Enable RLS
|
||||
alter table teams
|
||||
enable row level security;
|
||||
|
||||
-- 4. Create Policy
|
||||
create policy "Team members can update team details if they belong to the team."
|
||||
on teams
|
||||
for update using (
|
||||
auth.uid() in (
|
||||
select user_id from members
|
||||
where team_id = id
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
### Policies with security definer functions
|
||||
|
||||
Policies can also make use of `security definer functions`. This is useful in a many-to-many relationship where you want to restrict access to the linking table. Following the `teams` and `members` example from above, this example shows how you can use security definer function in combination with a policy to control access to the `members` table.
|
||||
|
||||
```sql
|
||||
-- 1. Follow example for 'Policies with joins' above
|
||||
|
||||
-- 2. Enable RLS
|
||||
alter table members
|
||||
enable row level security
|
||||
|
||||
-- 3. Create security definer function
|
||||
create or replace function get_teams_for_authenticated_user()
|
||||
returns setof bigint
|
||||
language sql
|
||||
security definer
|
||||
set search_path = public
|
||||
stable
|
||||
as $$
|
||||
select team_id
|
||||
from members
|
||||
where user_id = auth.uid()
|
||||
$$;
|
||||
|
||||
-- 4. Create Policy
|
||||
create policy "Team members can update team members if they belong to the team."
|
||||
on members
|
||||
for all using (
|
||||
team_id in (
|
||||
select get_teams_for_authenticated_user()
|
||||
)
|
||||
);
|
||||
|
||||
```
|
||||
|
||||
### Verifying email domains
|
||||
|
||||
Postgres has a function `right(string, n)` that returns the rightmost n characters of a string.
|
||||
You could use this to match staff member's email domains.
|
||||
|
||||
```sql
|
||||
-- 1. Create table
|
||||
create table leaderboard (
|
||||
id uuid references auth.users,
|
||||
high_score bigint
|
||||
);
|
||||
|
||||
-- 2. Enable RLS
|
||||
alter table leaderboard
|
||||
enable row level security;
|
||||
|
||||
-- 3. Create Policy
|
||||
create policy "Only Blizzard staff can update leaderboard"
|
||||
on leaderboard
|
||||
for update using (
|
||||
right(auth.email(), 13) = '@blizzard.com'
|
||||
);
|
||||
```
|
||||
|
||||
### Time to live for rows
|
||||
|
||||
Policies can also be used to implement TTL or time to live feature that you see in Instagram stories or Snapchat.
|
||||
In the following example, rows of `stories` table are available only if they have been created within the last 24 hours.
|
||||
|
||||
```sql
|
||||
-- 1. Create table
|
||||
create table if not exists stories (
|
||||
id uuid not null primary key DEFAULT uuid_generate_v4(),
|
||||
created_at timestamp with time zone default timezone('utc' :: text, now()) not null,
|
||||
content text not null
|
||||
);
|
||||
|
||||
-- 2. Enable RLS
|
||||
alter table stories
|
||||
enable row level security;
|
||||
|
||||
-- 3. Create Policy
|
||||
create policy "Stories are live for a day"
|
||||
on stories
|
||||
for select using (
|
||||
created_at > (current_timestamp - interval '1 day')
|
||||
);
|
||||
```
|
||||
|
||||
### Advanced policies
|
||||
|
||||
Use the full power of SQL to build extremely advanced rules.
|
||||
|
||||
In this example, we will create a `posts` and `comments` tables and then create a policy that depends on another policy.
|
||||
(In this case, the comments policy depends on the posts policy.)
|
||||
|
||||
```sql
|
||||
create table posts (
|
||||
id serial primary key,
|
||||
creator_id uuid not null references auth.users(id),
|
||||
title text not null,
|
||||
body text not null,
|
||||
publish_date date not null default now(),
|
||||
audience uuid[] null -- many to many table omitted for brevity
|
||||
);
|
||||
|
||||
create table comments (
|
||||
id serial primary key,
|
||||
post_id int not null references posts(id) on delete cascade,
|
||||
user_id uuid not null references auth.users(id),
|
||||
body text not null,
|
||||
comment_date date not null default now()
|
||||
);
|
||||
|
||||
create policy "Creator can see their own posts"
|
||||
on posts
|
||||
for select
|
||||
using (
|
||||
auth.uid() = posts.creator_id
|
||||
);
|
||||
|
||||
create policy "Logged in users can see the posts if they belong to the post 'audience'."
|
||||
on posts
|
||||
for select
|
||||
using (
|
||||
auth.uid() = any (posts.audience)
|
||||
);
|
||||
|
||||
create policy "Users can see all comments for posts they have access to."
|
||||
on comments
|
||||
for select
|
||||
using (
|
||||
exists (
|
||||
select 1 from posts
|
||||
where posts.id = comments.post_id
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
## Tips
|
||||
|
||||
### Disable realtime for private tables
|
||||
|
||||
Our realtime server doesn't provide per-user security. Until we build a more robust auth system for WebSockets, you can disable realtime functionality for any private tables. To do this, you can manage the underlying Postgres replication publication:
|
||||
|
||||
```sql
|
||||
/**
|
||||
* REALTIME SUBSCRIPTIONS
|
||||
* Only allow realtime listening on public tables.
|
||||
*/
|
||||
|
||||
begin;
|
||||
-- remove the realtime publication
|
||||
drop publication if exists supabase_realtime;
|
||||
|
||||
-- re-create the publication but don't enable it for any tables
|
||||
create publication supabase_realtime;
|
||||
commit;
|
||||
|
||||
-- add a table to the publication
|
||||
alter publication supabase_realtime add table products;
|
||||
|
||||
-- add other tables to the publication
|
||||
alter publication supabase_realtime add table posts;
|
||||
```
|
||||
|
||||
We're in the process of building [enhanced realtime security](https://github.com/supabase/walrus).
|
||||
|
||||
### You don't have to use policies
|
||||
|
||||
You can also put your authorization rules in your middleware, similar to how you would create security rules with any other `backend <-> middleware <-> frontend` architecture.
|
||||
|
||||
Policies are a tool. In the case of "serverless/Jamstack" setups, they are especially effective because you don't have to deploy any middleware at all.
|
||||
|
||||
However, if you want to use another authorization method for your applications, that's also fine. Supabase is "just Postgres", so if your application
|
||||
works with Postgres, then it also works with Supabase.
|
||||
|
||||
Tip: Make sure to enable RLS for all your tables, so that your tables are inaccessible. Then use the "Service" which we provide, which is designed to bypass RLS.
|
||||
|
||||
|
||||
### Never use a service key on the client
|
||||
|
||||
Supabase provides special "Service" keys, which can be used to bypass all RLS.
|
||||
These should never be used in the browser or exposed to customers, but they are useful for administrative tasks.
|
||||
+8
-2
@@ -136,10 +136,10 @@ module.exports = {
|
||||
label: 'Auth',
|
||||
collapsed: false,
|
||||
items: [
|
||||
'guides/auth/managing-user-data',
|
||||
'guides/auth/intro',
|
||||
{
|
||||
type: 'category',
|
||||
label: 'External Providers',
|
||||
label: 'Authentication',
|
||||
collapsed: true,
|
||||
items: [
|
||||
'guides/auth/auth-apple',
|
||||
@@ -154,6 +154,12 @@ module.exports = {
|
||||
'guides/auth/auth-twilio',
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'category',
|
||||
label: 'Authorization',
|
||||
collapsed: true,
|
||||
items: ['guides/auth/row-level-security', 'guides/auth/managing-user-data'],
|
||||
},
|
||||
{
|
||||
type: 'category',
|
||||
label: 'Deep Dive',
|
||||
|
||||
@@ -61,6 +61,8 @@
|
||||
--custom-primary-light: #65d9a5;
|
||||
--custom-primary-lighter: #9fe7c7;
|
||||
--custom-primary-lightest: #c5f1dd;
|
||||
--custom-primary-rgba: RGBA(36, 180, 126, 1);
|
||||
--custom-primary-rgb: 36, 180, 126;
|
||||
--custom-background-color: #1f1f1f;
|
||||
--custom-background-color-highlight: #fff;
|
||||
--custom-background-color-diff: #f5f6f7;
|
||||
@@ -1297,3 +1299,35 @@ h4.method-list-item-label .method-list-item-validation {
|
||||
overflow: hidden;
|
||||
border: 2px solid var(--custom-border-color);
|
||||
}
|
||||
|
||||
.badge--official {
|
||||
border-radius: 3px;
|
||||
font-size: 0.6rem;
|
||||
font-weight: 400;
|
||||
text-transform: uppercase;
|
||||
border-color: var(--custom-primary);
|
||||
background-color: rgba(var(--custom-primary-rgb), 0.8);
|
||||
}
|
||||
.badge--unofficial {
|
||||
border-radius: 3px;
|
||||
font-size: 0.6rem;
|
||||
font-weight: 400;
|
||||
text-transform: uppercase;
|
||||
color: var(--custom-content-color-lightest);
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
border-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
:root[data-theme='dark'] .badge--official {
|
||||
background-color: rgba(var(--custom-primary-rgb), 0.5);
|
||||
}
|
||||
:root[data-theme='dark'] .badge--unofficial {
|
||||
border-color: rgba(255, 255, 255, 0.1);
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
.code-block {
|
||||
border-radius: 3px;
|
||||
font-family: 'office code pro';
|
||||
border: 1px solid var(--custom-border-color);
|
||||
padding: 2px 6px;
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
const authProviders = [
|
||||
// {
|
||||
// name: 'Email',
|
||||
// // logo: '/img/libraries/dart-icon.svg',
|
||||
// href: '/docs/guides/auth/auth-apple',
|
||||
// official: true,
|
||||
// supporter: 'Supabase',
|
||||
// platform: true,
|
||||
// selfHosted: true,
|
||||
// },
|
||||
// {
|
||||
// name: 'Magic Links',
|
||||
// // logo: '/img/libraries/dart-icon.svg',
|
||||
// href: '/docs/guides/auth/auth-apple',
|
||||
// official: true,
|
||||
// supporter: 'Supabase',
|
||||
// platform: true,
|
||||
// selfHosted: true,
|
||||
// },
|
||||
{
|
||||
name: 'Apple',
|
||||
// logo: '/img/libraries/dart-icon.svg',
|
||||
href: '/docs/guides/auth/auth-apple',
|
||||
official: true,
|
||||
supporter: 'Supabase',
|
||||
platform: true,
|
||||
selfHosted: true,
|
||||
},
|
||||
{
|
||||
name: 'Azure',
|
||||
// logo: '/img/libraries/dart-icon.svg',
|
||||
href: '/docs/guides/auth/auth-apple',
|
||||
official: false,
|
||||
supporter: 'TBD',
|
||||
platform: false,
|
||||
selfHosted: true,
|
||||
},
|
||||
{
|
||||
name: 'Bitbucket',
|
||||
// logo: '/img/libraries/dart-icon.svg',
|
||||
href: '/docs/guides/auth/auth-bitbucket',
|
||||
official: true,
|
||||
supporter: 'Supabase',
|
||||
platform: true,
|
||||
selfHosted: true,
|
||||
},
|
||||
{
|
||||
name: 'Discord',
|
||||
// logo: '/img/libraries/dart-icon.svg',
|
||||
href: '/docs/guides/auth/auth-discord',
|
||||
official: true,
|
||||
supporter: 'Supabase',
|
||||
platform: true,
|
||||
selfHosted: true,
|
||||
},
|
||||
{
|
||||
name: 'Facebook',
|
||||
// logo: '/img/libraries/dart-icon.svg',
|
||||
href: '/docs/guides/auth/auth-facebook',
|
||||
official: true,
|
||||
supporter: 'Supabase',
|
||||
platform: true,
|
||||
selfHosted: true,
|
||||
},
|
||||
{
|
||||
name: 'GitHub',
|
||||
// logo: '/img/libraries/dart-icon.svg',
|
||||
href: '/docs/guides/auth/auth-github',
|
||||
official: true,
|
||||
supporter: 'Supabase',
|
||||
platform: true,
|
||||
selfHosted: true,
|
||||
},
|
||||
{
|
||||
name: 'GitLab',
|
||||
// logo: '/img/libraries/dart-icon.svg',
|
||||
href: '/docs/guides/auth/auth-gitlab',
|
||||
official: true,
|
||||
supporter: 'Supabase',
|
||||
platform: true,
|
||||
selfHosted: true,
|
||||
},
|
||||
{
|
||||
name: 'Google',
|
||||
// logo: '/img/libraries/dart-icon.svg',
|
||||
href: '/docs/guides/auth/auth-google',
|
||||
official: true,
|
||||
supporter: 'Supabase',
|
||||
platform: true,
|
||||
selfHosted: true,
|
||||
},
|
||||
// {
|
||||
// name: 'MessageBird',
|
||||
// // logo: '/img/libraries/dart-icon.svg',
|
||||
// href: '/docs/guides/auth/auth-twilio',
|
||||
// official: false,
|
||||
// supporter: 'Messagebird',
|
||||
// platform: false,
|
||||
// selfHosted: true,
|
||||
// },
|
||||
{
|
||||
name: 'Twitter',
|
||||
// logo: '/img/libraries/dart-icon.svg',
|
||||
href: '/docs/guides/auth/auth-twitter',
|
||||
official: true,
|
||||
supporter: 'Supabase',
|
||||
platform: true,
|
||||
selfHosted: true,
|
||||
},
|
||||
{
|
||||
name: 'Twitch',
|
||||
// logo: '/img/libraries/dart-icon.svg',
|
||||
href: '/docs/guides/auth/auth-twitch',
|
||||
official: true,
|
||||
supporter: 'Supabase',
|
||||
platform: true,
|
||||
selfHosted: true,
|
||||
},
|
||||
{
|
||||
name: 'Twilio',
|
||||
// logo: '/img/libraries/dart-icon.svg',
|
||||
href: '/docs/guides/auth/auth-twilio',
|
||||
official: true,
|
||||
supporter: 'Supabase',
|
||||
platform: true,
|
||||
selfHosted: true,
|
||||
},
|
||||
]
|
||||
|
||||
export default authProviders
|
||||
Reference in new issue
Block a user