From 150660819a44648968e0d5c1f3d112827108cd09 Mon Sep 17 00:00:00 2001 From: Ilia Denisov Date: Fri, 3 Jul 2026 10:01:54 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20sign-in=20methods=20matrix=20=E2=80=94?= =?UTF-8?q?=20unlink=20+=20change-email?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FUNCTIONAL (+ru): drop the stale 'linking UI hidden' note; document the profile sign-in-methods matrix, the unlink last-identity guard, and the non-disclosing atomic email change. ARCHITECTURE: unlink + change-email behaviour and the purpose=change deeplink branch. --- docs/ARCHITECTURE.md | 14 ++++++++++++-- docs/FUNCTIONAL.md | 14 ++++++++++---- docs/FUNCTIONAL_ru.md | 14 ++++++++++---- 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 223bc07..f1376c5 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -243,8 +243,8 @@ arrive from a platform rather than completing a mandatory registration). also carries a **one-tap confirm deeplink** (`/app/#/confirm/`, an opaque 256-bit token stored only as its SHA-256, 12-hour TTL): a login mints a session in the browser that opens it (magic-link), a link confirms the identity and emits a `notify` - profile-refresh to the in-app session, and a would-be merge is deferred to the - interactive flow. The confirm runs on load; the token rides the URL fragment (never + profile-refresh to the in-app session, a change switches the account's email in place, + and a would-be merge is deferred to the interactive flow. The confirm runs on load; the token rides the URL fragment (never sent to the server), so a plain link prefetch cannot reach it, and the manual six-digit code is the fallback if an aggressive scanner runs the page. An **email-login** account is created flagged `is_guest` and stays reapable until the @@ -263,6 +263,16 @@ arrive from a platform rather than completing a mandatory registration). is revealed **only after** the proof is verified and is performed behind an explicit, irreversible confirmation. A free identity is simply attached (and a guest is promoted to durable, clearing `is_guest`). +- **Unlink** detaches a platform identity (`telegram`/`vk`) from the profile. The + backend **refuses removing the last identity** (`ErrLastIdentity`), so an account + never becomes unreachable; the UI mirrors the guard by hiding Unlink when only one + method remains. **Email is never unlinked — it is changed.** +- **Change email** mails a confirm-code (`purpose=change`) to the new address on the + authenticated account and, on confirm (code or one-tap deeplink), **atomically + replaces** the account's email identity with the new one, freeing the old address. A + new address already confirmed by **another** account is refused **without disclosure** + (a neutral "check the address or contact support") and **never merged** — the anti- + enumeration check is only reachable by someone who controls the new mailbox. - **Merge** retires the account that owns the linked identity into the **current** account, in a single transaction (`internal/accountmerge`): statistics summed (counters incl. moves/hints added, max points kept, and the per-variant best moves diff --git a/docs/FUNCTIONAL.md b/docs/FUNCTIONAL.md index ec82aef..c517b61 100644 --- a/docs/FUNCTIONAL.md +++ b/docs/FUNCTIONAL.md @@ -96,10 +96,6 @@ reconnect), and pending reads resume on their own — the interface stays usable flashing a red banner each time. ### Accounts, linking & merge -_Sign-in is currently provider-only, so the in-profile linking UI is temporarily hidden; it -returns once the anonymous `/app/` guest (whose upgrade path this is) ships. The flow below -describes it for when it does._ - First platform contact auto-provisions a durable account. From the profile a player links an email (via a confirm code) or their Telegram (via the web sign-in); a guest who links their first identity becomes a durable account. The "already taken" status @@ -111,6 +107,16 @@ when a guest links an identity that already has a durable account, where the dur account is kept and the guest's games move into it. A merge is blocked only while the two accounts share a game still in progress. +The profile lists the account's **sign-in methods**. On the web a player can add +Telegram; adding VK on the web is not offered yet, and inside a Mini App the host +platform is already linked. A linked provider can be **unlinked** — except the last +remaining sign-in method, which is refused so the account stays reachable. **Email is +never unlinked; it is changed**: the player enters a new address, confirms a code +mailed to it, and the account switches to it atomically, freeing the old address. A new +address that already belongs to another account is refused with a neutral "check the +address or contact support" — the switch never merges and never reveals the other +account. + ### Lobby & matchmaking On a cold open the lobby greets the player with a brief **loading splash** — Scrabble tiles spelling **ЭРУДИТ / ЗАГРУЗКА / ОЖИДАНИЕ** as a small crossword — that clears the moment the diff --git a/docs/FUNCTIONAL_ru.md b/docs/FUNCTIONAL_ru.md index 410a46a..1d9c52b 100644 --- a/docs/FUNCTIONAL_ru.md +++ b/docs/FUNCTIONAL_ru.md @@ -100,10 +100,6 @@ Telegram держит **единого бота**: все игроки поль рабочим вместо красного баннера каждый раз. ### Аккаунты, привязка и слияние -_Вход сейчас только через провайдера, поэтому UI привязки в профиле временно скрыт; он -вернётся, когда появится анонимный `/app/`-гость (для апгрейда которого он и нужен). Описание -ниже — на этот случай._ - Первый контакт с платформы заводит постоянный аккаунт. Из профиля игрок привязывает email (по confirm-коду) или свой Telegram (через веб-вход); гость, привязавший первую личность, становится постоянным аккаунтом. Факт «личность уже @@ -115,6 +111,16 @@ _Вход сейчас только через провайдера, поэто тогда сохраняется постоянный аккаунт, а игры гостя переходят в него. Слияние запрещено, только пока у аккаунтов есть общая незавершённая игра. +В профиле перечислены **способы входа** аккаунта. В вебе игрок может добавить +Telegram; добавление VK в вебе пока не предлагается, а внутри Mini App платформа-хозяин +уже привязана. Привязанного провайдера можно **отвязать** — кроме последнего +оставшегося способа входа: он не отвязывается, чтобы аккаунт оставался достижимым. +**Email не отвязывают — его меняют**: игрок вводит новый адрес, подтверждает код, +отправленный на него, и аккаунт атомарно переключается на новый адрес, освобождая +старый. Новый адрес, уже принадлежащий другому аккаунту, отклоняется нейтральным +«проверьте правильность e-mail или обратитесь в поддержку» — смена никогда не сливает +аккаунты и не раскрывает чужой. + ### Лобби и подбор При холодном запуске лобби встречает игрока короткой **заставкой загрузки** — фишки Scrabble складывают небольшой кроссворд из слов **ЭРУДИТ / ЗАГРУЗКА / ОЖИДАНИЕ** — и она исчезает, как