# Email OTP Auth Implementation Plan > **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Включить реальный вход в приложение по одноразовому пинкоду из письма через Supabase только для заранее заведенных пользователей с ролями `admin` и `logistician`. **Architecture:** Фронтенд остается на текущем `AuthContext` и `OtpLoginForm`, но demo-подсказки перестают влиять на рабочий auth flow. Supabase Auth отвечает за отправку email OTP и создание session, а приложение после подтверждения кода читает профиль только из `public.users` и `roles`. Новые пользователи через форму не создаются: доступ есть только у тех email, которые заранее заведены в `auth.users` и синхронизированы с `public.users`. **Tech Stack:** React 18, Vite, Supabase JS v2, Supabase Auth Email OTP, Supabase SQL, Vitest. --- ## File Structure - Modify: `src/context/AuthContext.jsx` — отключить автосоздание пользователей, ужесточить рабочий OTP-flow, обработать отсутствие профиля. - Modify: `src/context/AuthContext.test.js` — покрыть новую логику для рабочего режима и сохранить demo-фолбэк. - Modify: `src/components/auth/OtpLoginForm.jsx` — убрать рабочую зависимость от выбора demo-роли и уточнить тексты для OTP-входа. - Modify: `src/components/auth/OtpLoginForm.test.jsx` — скорректировать ожидания под новый рабочий сценарий. - Modify: `src/pages/LoginPage.jsx` — оставить линейный сценарий `email -> код -> вход`, без влияния роли в production-режиме. - Create: `docs/operations/supabase-email-otp-auth.md` — пошаговая настройка Supabase Auth, пользователей и ролей. - Reference: `src/supabaseClient.js` — проверить, что frontend env уже подаются корректно. - Reference: `supabase/schema.sql` — использовать существующие `roles`, `users`, `handle_new_user()` и не дублировать auth-механику. ## Chunk 1: Frontend Auth Flow Tightening ### Task 1: Зафиксировать ожидаемое поведение auth-flow тестами **Files:** - Modify: `src/context/AuthContext.test.js` - Modify: `src/components/auth/OtpLoginForm.test.jsx` - [ ] **Step 1: Add failing tests for production OTP constraints** Добавить проверки на: - в рабочем режиме email не подменяется demo-значением; - форма не подсказывает выбор роли как источник прав; - текст формы объясняет вход по email-коду; - demo-режим остается рабочим fallback. - [ ] **Step 2: Run targeted tests to verify they fail** Run: `npm test -- src/context/AuthContext.test.js src/components/auth/OtpLoginForm.test.jsx` Expected: FAIL on outdated demo-oriented behavior. - [ ] **Step 3: Keep existing demo tests if still valid** Если текущие тесты про `resolveDemoUser` и `resolveLoginEmail` остаются полезными, сохранить их и добавить рядом production-specific expectations вместо полной замены. - [ ] **Step 4: Re-run targeted tests after each test update** Run: `npm test -- src/context/AuthContext.test.js src/components/auth/OtpLoginForm.test.jsx` Expected: FAIL only for not-yet-implemented UI/auth changes. ### Task 2: Отключить саморегистрацию через OTP-форму **Files:** - Modify: `src/context/AuthContext.jsx` - Modify: `src/pages/LoginPage.jsx` - [ ] **Step 1: Write the minimal production auth contract** В `AuthContext` зафиксировать: - `requestOtp()` вызывает `supabase.auth.signInWithOtp` - `options.shouldCreateUser` больше не передается как `true` - `pendingEmail` остается текущим entered email - demo-mode behavior не ломается - [ ] **Step 2: Make the implementation minimal** Изменить рабочий режим так, чтобы: - логин шел только для уже существующего email; - новая учетная запись не создавалась автоматически; - текст ошибки из Supabase пробрасывался наверх без маскировки. - [ ] **Step 3: Verify the targeted auth tests** Run: `npm test -- src/context/AuthContext.test.js` Expected: PASS - [ ] **Step 4: Sanity-check login screen code** Убедиться, что `src/pages/LoginPage.jsx` не передает рабочую роль из UI в production-сценарий и не связывает доступ с выбором `roleHint`. ### Task 3: Привести OTP-форму к рабочему сценарию доступа по списку email **Files:** - Modify: `src/components/auth/OtpLoginForm.jsx` - Modify: `src/components/auth/OtpLoginForm.test.jsx` - [ ] **Step 1: Remove role selection from the production story** Сохранить `roleHint` только как demo-only affordance, но не показывать пользователю, что роль задается при реальном входе. - [ ] **Step 2: Update copy for real OTP flow** Тексты формы должны говорить: - email вводится пользователем; - код приходит на почту; - доступ определяется учетной записью в системе, а не выбором роли. - [ ] **Step 3: Keep demo fallback explicit but isolated** Если `isDemoMode === true`, оставить подсказку и поведение demo-режима, но визуально отделить его от реального сценария. - [ ] **Step 4: Re-run form tests** Run: `npm test -- src/components/auth/OtpLoginForm.test.jsx` Expected: PASS ## Chunk 2: Profile Resolution And Access Guard ### Task 4: Обработать сценарий “auth user есть, профиля в public.users нет” **Files:** - Modify: `src/context/AuthContext.jsx` - Modify: `src/context/AuthContext.test.js` - [ ] **Step 1: Add a failing test for missing profile** Проверить сценарий: - Supabase session создана; - запрос в `public.users` не нашел профиль или вернул ошибку; - пользователь не считается успешно авторизованным в приложении без понятной реакции. - [ ] **Step 2: Implement minimal guard** В `onAuthStateChange`: - если профиль не найден, не выдавать рабочий `user` в приложение; - сохранять понятную ошибку или вынуждать повторный вход; - не падать молча. - [ ] **Step 3: Verify tests** Run: `npm test -- src/context/AuthContext.test.js` Expected: PASS ### Task 5: Уточнить контракт роли и профиля **Files:** - Modify: `src/context/AuthContext.jsx` - Reference: `supabase/schema.sql` - [ ] **Step 1: Verify role source** Роль должна читаться только из `public.users -> roles(name)`. - [ ] **Step 2: Keep fallback conservative** Если роль не пришла, не повышать доступ по умолчанию; допустим fallback только в безопасный минимум (`manager`) либо ошибка доступа, в зависимости от того, что окажется проще и безопаснее после чтения кода. - [ ] **Step 3: Re-run auth tests** Run: `npm test -- src/context/AuthContext.test.js` Expected: PASS ## Chunk 3: Supabase Setup And Data Seeding ### Task 6: Подготовить рабочую инструкцию по настройке Supabase Auth **Files:** - Create: `docs/operations/supabase-email-otp-auth.md` - [ ] **Step 1: Document dashboard settings** Описать: - где включить email OTP; - что проверить в email auth settings; - что self-signup должен быть выключен, если используется whitelist-only access. - [ ] **Step 2: Document required frontend env** Зафиксировать: - `VITE_SUPABASE_URL` - `VITE_SUPABASE_ANON_KEY` - [ ] **Step 3: Document function secrets separately** Коротко пояснить: - frontend env идут в `.env.local`; - Edge Function secrets задаются через Supabase secrets/dashboard; - для OTP-входа frontend не использует `SUPABASE_SERVICE_ROLE_KEY`. ### Task 7: Подготовить SQL/операционную часть для двух пользователей **Files:** - Create or extend: `docs/operations/supabase-email-otp-auth.md` - Reference: `supabase/schema.sql` - [ ] **Step 1: Describe manual user creation in Supabase Auth** Нужно заранее создать: - `skylanguage@yandex.ru` - `mk7029953@yandex.ru` - [ ] **Step 2: Document role binding in public.users** Подготовить SQL-пример или пошаговое описание, как этим пользователям назначить: - `skylanguage@yandex.ru` -> `admin` - `mk7029953@yandex.ru` -> `logistician` - [ ] **Step 3: Check trigger compatibility** Убедиться по схеме, что `handle_new_user()`: - не ломает вручную заведенных пользователей; - корректно пишет профиль в `public.users`; - использует `raw_user_meta_data ->> 'role'`, если оно есть. - [ ] **Step 4: Add operator verification checklist** Проверка после настройки: - код приходит на обе почты; - `admin` видит админский раздел; - `logistician` видит логистический контур; - неизвестный email не получает доступ. ## Chunk 4: End-To-End Verification ### Task 8: Полная проверка реализации **Files:** - Reference: `src/context/AuthContext.jsx` - Reference: `src/components/auth/OtpLoginForm.jsx` - Reference: `src/pages/LoginPage.jsx` - Reference: `docs/operations/supabase-email-otp-auth.md` - [ ] **Step 1: Run focused frontend tests** Run: `npm test -- src/context/AuthContext.test.js src/components/auth/OtpLoginForm.test.jsx` Expected: PASS - [ ] **Step 2: Run full test suite** Run: `npm test` Expected: PASS - [ ] **Step 3: Run linter** Run: `npm run lint` Expected: PASS - [ ] **Step 4: Run production build** Run: `npm run build` Expected: PASS - [ ] **Step 5: Manual Supabase validation** Проверить руками: - отправка кода на `skylanguage@yandex.ru` - отправка кода на `mk7029953@yandex.ru` - успешный вход по пинкоду - корректная роль после входа - отказ для email вне whitelist