2.8 KiB
2.8 KiB
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
- Open your Supabase project.
- Go to
Authenticationand confirm email OTP / email login is enabled. - Check the email provider settings so OTP messages are deliverable.
- Disable self-signup if the project should stay whitelist-only.
- Make sure the two allowed emails exist in Supabase Auth before testing login.
Frontend environment
The frontend only needs these variables in .env.local:
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.rumk7029953@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:
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:
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
adminreaches the admin area after sign-in. - Confirm
logisticianreaches 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.usershas no matching profile row.