mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 18:05:11 +03:00
384 lines
12 KiB
Plaintext
384 lines
12 KiB
Plaintext
---
|
|
id: auth
|
|
title: Auth
|
|
description: Use Supabase to Authenticate and Authorize your users.
|
|
---
|
|
|
|
### User Management
|
|
|
|
Supabase makes it simple to manage your users.
|
|
|
|
<video width="99%" muted playsInline controls="true">
|
|
<source src="/videos/auth-zoom2.mp4" type="video/mp4" muted playsInline />
|
|
</video>
|
|
|
|
When a user signs up, Supabase assigns them a unique ID. You can reference this ID anywhere in your database. For example, you might create a `profiles` table referencing the user using a `user_id` field.
|
|
|
|
Supabase provides the routes to [sign up](/docs/reference/javascript/auth-signup), [log in](/docs/reference/javascript/auth-signin),
|
|
[log out](/docs/reference/javascript/auth-signout), and manage users in your apps and websites.
|
|
|
|
|
|
### Third Party Logins
|
|
|
|
We currently support the following OAuth providers:
|
|
- Google
|
|
- Github
|
|
- Gitlab
|
|
- Azure
|
|
- Facebook
|
|
- Bitbucket
|
|
|
|

|
|
|
|
You can enable providers by navigating to Authentication > Settings > External OAuth Providers and inputting your `Client ID` and `Secret` for each.
|
|
|
|
To fetch these you need to:
|
|
|
|
1. Generate `Client ID` and `Secret` ([google](https://console.developers.google.com/apis/credentials), [github](https://github.com/settings/applications/new), [gitlab](https://gitlab.com/oauth/applications), [bitbucket](https://support.atlassian.com/bitbucket-cloud/docs/use-oauth-on-bitbucket-cloud/))
|
|
2. Enter Authorized Redirect URI: `https://<your-project>.supabase.co/auth/v1/callback` on provider dashboard
|
|
|
|
### Row Level Security
|
|
|
|
Authentication only gets you so far. When you need granular authorization rules, nothing beats PostgreSQL's [Row Level Security](https://www.postgresql.org/docs/current/ddl-rowsecurity.html). Supabase makes it simple to turn RLS on and off.
|
|
|
|
<video width="99%" muted playsInline controls="true">
|
|
<source src="/videos/rls-zoom2.mp4" type="video/mp4" muted playsInline />
|
|
</video>
|
|
|
|
|
|
### Policies
|
|
|
|
[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.
|
|
|
|
<video width="99%" muted playsInline controls="true">
|
|
<source src="/videos/policies-zoom2.mp4" type="video/mp4" muted playsInline />
|
|
</video>
|
|
|
|
With policies, your database becomes the rules engine. Instead of repetitively filtering your queries, like this ...
|
|
|
|
```js
|
|
const loggedInUserId = 'd0714948'
|
|
let { data, error } = await supabase
|
|
.from('users')
|
|
.select('user_id, name')
|
|
.eq('user_id', loggedInUserId)
|
|
|
|
// console.log(data)
|
|
// => { id: 'd0714948', name: 'Jane' }
|
|
```
|
|
|
|
... you can simply define a rule on your database table, `auth.uid() = user_id`, and your request will return the rows which pass the rule, even when you remove the filter from your middleware:
|
|
|
|
```js
|
|
let user = await supabase
|
|
.from('users')
|
|
.select('user_id, name')
|
|
|
|
// console.log(data)
|
|
// 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
|
|
|
|
1. A user signs up. Supabase creates a new user in the `auth.users` table.
|
|
2. Supabase returns a new JWT, which contains the user's `UUID`.
|
|
3. Every request to your database also sends the JWT.
|
|
4. Postgres inspects the JWT to determine the user making the request.
|
|
5. The user's UID can be used in Policies to restrict access to rows.
|
|
|
|
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 Row Level Security. Each policy is attached to a table, and the policy is executed
|
|
every time a the 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 Row Level Security
|
|
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_user(user_id uuid)
|
|
returns setof bigint as $$
|
|
select team_id
|
|
from members
|
|
where user_id = $1
|
|
$$ stable language sql security definer;
|
|
|
|
-- 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_user(auth.uid())
|
|
)
|
|
);
|
|
|
|
```
|
|
|
|
### 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'
|
|
);
|
|
```
|
|
|
|
### Advanced Policies
|
|
|
|
Use the full power of SQL to build extremely advanced rules.
|
|
|
|
In this example we will create a `posts` and `comments` database 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 ommited 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/Jastack" 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 Row Level Security for all your tables, so that your tables are inaccessible. Then use the "Service" which we provide.
|
|
"Service" are 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 Row Level Security.
|
|
These should never be used in the browser or exposed to customers, but they are useful for administrative tasks.
|
|
|
|
### Create a `public.users` table.
|
|
|
|
Even though Supabase provides an `auth.users` table, it is helpful to also create a users table in the `public` schema, which uses the same UID Primary Key as the `auth.users`.
|
|
For security purposes, the `auth` schema is not exposed on the auto-generated API. Creating a `public.users` table allows you to interact via the Supabase client -
|
|
especially useful for cross-table queries.
|
|
|
|
Pro tip: if you want to add a row to your `public.users` table every time a user signs up, you can use triggers. For example:
|
|
|
|
```sql
|
|
-- inserts a row into public.users
|
|
create function public.handle_new_user()
|
|
returns trigger as $$
|
|
begin
|
|
insert into public.users (id)
|
|
values (new.id);
|
|
return new;
|
|
end;
|
|
$$ language plpgsql security definer;
|
|
|
|
-- 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();
|
|
```
|
|
|
|
### 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 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 are aiming to deliver enhanced realtime security by Q1 2021.
|
|
|
|
## Next steps
|
|
|
|
- Read more about [Row Level Security](https://www.postgresql.org/docs/current/ddl-rowsecurity.html)
|
|
- Sign in: [app.supabase.io](https://app.supabase.io)
|