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

12 KiB
Raw Blame History

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