supersam/docs/superpowers/plans/2026-04-09-email-otp-auth.md

254 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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