# 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.