Загрузка данных


Вердикт: **это баг на стороне auth/session owner, а не допустимое продуктовое поведение.**

Продуктовая модель явно требует одновременной работы нескольких устройств одного пользователя:

- `SPEC-sec-001` включает multi-device с cross-signing.
- [secure-multidevice-test-cases.md](/Users/anzelika/messenger/docs/frontend_dev/secure-multidevice-test-cases.md:49) предписывает использовать один JWT в двух окнах `U1-A` и `U1-B`.
- MD-03 прямо требует независимую регистрацию второго устройства того же пользователя; MD-05–11 требуют параллельного SAS/enrollment транспорта между ними.

Политика «вход на втором устройстве отзывает токен первого» с этим несовместима и в Messenger не зафиксирована.

Точная причина на границе Messenger установлена:

1. Все E2EE endpoint’ы проходят через единый `auth.Handle` ещё до device proof и бизнес-авторизации: [external_api_server.go](/Users/anzelika/messenger/external-api/internal/adapters/in/http/external_api_server/external_api_server.go:59).
2. Для каждого запроса middleware передаёт исходный bearer token в `AuthenticateUserByToken`; любая ошибка превращается в HTTP 401: [auth_middleware.go](/Users/anzelika/messenger/external-api/internal/adapters/in/http/auth_middleware/auth_middleware.go:36).
3. Реальный валидатор здесь не Messenger: он вызывает `webapp-service` RPC `GetUserByJwtToken`: [webapp_service_authenticator.go](/Users/anzelika/messenger/shared/adapters/out/authenticator/webapp_service_authenticator/webapp_service_authenticator.go:34).
4. В Messenger нет session store, revoke, token version, JTI, JWT cache или device-bound access token validation. Он также не создаёт access token при enrollment/login.
5. RPC-контракт даже возвращает `session_id`, но текущий adapter его не использует: [api.proto](/Users/anzelika/messenger/api/webapp-service-internal-api/api.proto:37).

Следовательно, 318 одинаковых 401 — это не пять разных E2EE authz-ошибок и не `ready`/SAS rule. Это повторный отказ upstream `webapp-service` валидировать token первого iPhone. Хронология подтверждает: отказ начался до `ready`, значит enrollment/SAS не мог быть непосредственным серверным триггером в Messenger.

Что именно отозвано и почему, по этому репозиторию определить нельзя: token/JTI/session ID не логируются и не сохраняются здесь, а исходный `webapp-service` отсутствует из workspace. Вероятная точка дефекта — login/session-replacement или cache invalidation в `webapp-service`/auth gateway: новый login для пользователя инвалидирует все активные user sessions вместо ровно целевой сессии (или cache отдаёт такое состояние).

Ничего в Messenger не менял: локально «обойти» 401 кэшированием старого bearer token было бы небезопасно и не исправило бы канонический revoke.

Нужная правка у владельца `webapp-service`:

- session должна иметь независимый `session_id`/JTI на устройство;
- login второго iPhone не должен отзывать JTI первого;
- logout/revoke должен удалять только конкретную session/JTI, кроме явно запрошенного “logout all devices”;
- cache key должен быть по token/JTI, а invalidation — по конкретной session, не по `user_id`;
- добавить audit: `user_id`, hash JTI/session ID, reason (`login`, `logout`, `revoke`, expiry, cache invalidation), caller/device metadata — без raw token.

Минимальная проверка для того сервиса: выпустить два токена одного пользователя на `14e42ce07a` и `fdfd820e57`; после второго login оба должны успешно проходить `GetUserByJwtToken`, а все пять перечисленных E2EE endpoint’ов — возвращать 2xx. Затем пройти `request → ready → start → SAS → key transfer → done=true`.

Локальный merge по предыдущей задаче всё ещё не завершён коммитом; новых изменений для этой проверки я в него не добавлял.