mirror of
https://github.com/supabase/supabase.git
synced 2026-07-03 09:14:28 +08:00
108 lines
3.3 KiB
Plaintext
108 lines
3.3 KiB
Plaintext
---
|
|
id: managing-user-data
|
|
title: Managing User Data
|
|
description: Securing your user data with Row Level Security.
|
|
---
|
|
|
|
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 that you want to access via the API.
|
|
|
|
## Creating user tables
|
|
|
|
When you create tables to store user data, it's helpful to reference the `auth.users` table in the primary key. This ensures data integrity.
|
|
|
|
For example, a `public.profiles` table might look like this:
|
|
|
|
```sql
|
|
create table public.profiles (
|
|
id uuid references auth.users not null,
|
|
first_name text,
|
|
last_name text,
|
|
|
|
primary key (id)
|
|
);
|
|
|
|
alter table public.profiles enable row level security;
|
|
```
|
|
|
|
## Public access
|
|
|
|
Since Row Level Security is enabled, this table is accessible via the API but no data will be returned unless we set up some Policies.
|
|
If we wanted the data to be _readable_ by everyone but only allow logged-in users to update their own data, the Policies would look like this:
|
|
|
|
```sql
|
|
create policy "Public profiles are viewable by everyone."
|
|
on profiles for select
|
|
using ( true );
|
|
|
|
create policy "Users can insert their own profile."
|
|
on profiles for insert
|
|
with check ( auth.uid() = id );
|
|
|
|
create policy "Users can update own profile."
|
|
on profiles for update
|
|
using ( auth.uid() = id );
|
|
```
|
|
|
|
## Private access
|
|
|
|
If the data should only be _readable_ by the user who owns the data, we just need to change the `for select` query above.
|
|
|
|
```sql
|
|
create policy "Profiles are viewable by users who created them."
|
|
on profiles for select
|
|
using ( auth.uid() = id );
|
|
```
|
|
|
|
The nice thing about this pattern? We can now query this table via the API and we don't need to include data filters in our API queries - the Policies will handle that for us:
|
|
|
|
```js
|
|
// This will return nothing while the user is logged out
|
|
const { data } = await supabase
|
|
.from('profiles')
|
|
.select('id, username, avatar_url, website')
|
|
|
|
// After the user is logged in, this will only return
|
|
// the logged-in user's data - in this case a single row
|
|
const { error } = await supabase.auth.signIn({ email })
|
|
const { data: profile } = await supabase
|
|
.from('profiles')
|
|
.select('id, username, avatar_url, website')
|
|
```
|
|
|
|
## Bypassing Row Level Security
|
|
|
|
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.
|
|
|
|
## Advanced techniques
|
|
|
|
### Using triggers
|
|
|
|
If you want to add a row to your `public.profiles` 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 set search_path = public
|
|
as $$
|
|
begin
|
|
insert into public.profiles (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();
|
|
```
|