88 lines
2.8 KiB
Markdown
88 lines
2.8 KiB
Markdown
# Supabase Email OTP Auth
|
|
|
|
This guide covers the whitelist-only email OTP login flow for the app.
|
|
|
|
## What changes in this flow
|
|
|
|
- Users sign in with a one-time code sent to email.
|
|
- The app only accepts accounts that already exist in `auth.users`.
|
|
- The app reads the app role from `public.users -> public.roles`.
|
|
- Self-signup should be disabled if you want whitelist-only access.
|
|
|
|
## Supabase dashboard settings
|
|
|
|
1. Open your Supabase project.
|
|
2. Go to `Authentication` and confirm email OTP / email login is enabled.
|
|
3. Check the email provider settings so OTP messages are deliverable.
|
|
4. Disable self-signup if the project should stay whitelist-only.
|
|
5. Make sure the two allowed emails exist in Supabase Auth before testing login.
|
|
|
|
## Frontend environment
|
|
|
|
The frontend only needs these variables in `.env.local`:
|
|
|
|
```bash
|
|
VITE_SUPABASE_URL=...
|
|
VITE_SUPABASE_ANON_KEY=...
|
|
```
|
|
|
|
Do not put `SUPABASE_SERVICE_ROLE_KEY` in the frontend. If an Edge Function needs secrets, add them in the Supabase dashboard or via `supabase secrets`.
|
|
|
|
## Seed the allowed users
|
|
|
|
Create these Auth users first:
|
|
|
|
- `skylanguage@yandex.ru`
|
|
- `mk7029953@yandex.ru`
|
|
|
|
The `public.handle_new_user()` trigger will create the matching row in `public.users` when a new `auth.users` row is inserted. It already reads `raw_user_meta_data ->> 'role'`, so if you set metadata during user creation, the role can be applied automatically.
|
|
|
|
If the user already exists in `auth.users`, bind the role in `public.users` with SQL:
|
|
|
|
```sql
|
|
update public.users u
|
|
set role_id = r.id
|
|
from public.roles r
|
|
where u.email = 'skylanguage@yandex.ru'
|
|
and r.name = 'admin';
|
|
|
|
update public.users u
|
|
set role_id = r.id
|
|
from public.roles r
|
|
where u.email = 'mk7029953@yandex.ru'
|
|
and r.name = 'logistician';
|
|
```
|
|
|
|
If a user exists in Auth but the profile row is missing in `public.users`, recreate it manually:
|
|
|
|
```sql
|
|
insert into public.users (id, email, name, role_id, last_login)
|
|
select
|
|
au.id,
|
|
au.email,
|
|
coalesce(au.raw_user_meta_data ->> 'name', split_part(au.email, '@', 1)),
|
|
r.id,
|
|
timezone('utc', now())
|
|
from auth.users au
|
|
join public.roles r on r.name = case
|
|
when au.email = 'skylanguage@yandex.ru' then 'admin'
|
|
when au.email = 'mk7029953@yandex.ru' then 'logistician'
|
|
else 'manager'
|
|
end
|
|
where au.email in ('skylanguage@yandex.ru', 'mk7029953@yandex.ru')
|
|
on conflict (id) do update
|
|
set email = excluded.email,
|
|
name = excluded.name,
|
|
role_id = excluded.role_id,
|
|
last_login = excluded.last_login;
|
|
```
|
|
|
|
## Sanity checks
|
|
|
|
- Send an OTP to `skylanguage@yandex.ru`.
|
|
- Send an OTP to `mk7029953@yandex.ru`.
|
|
- Confirm `admin` reaches the admin area after sign-in.
|
|
- Confirm `logistician` reaches the logistics area after sign-in.
|
|
- Confirm an email outside the whitelist is rejected.
|
|
- Confirm the app shows an error if Auth has a session but `public.users` has no matching profile row.
|