docs: sign-in methods matrix — unlink + change-email
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.
This commit is contained in:
+12
-2
@@ -243,8 +243,8 @@ arrive from a platform rather than completing a mandatory registration).
|
|||||||
also carries a **one-tap confirm deeplink** (`/app/#/confirm/<token>`, an opaque
|
also carries a **one-tap confirm deeplink** (`/app/#/confirm/<token>`, an opaque
|
||||||
256-bit token stored only as its SHA-256, 12-hour TTL): a login mints a session in the
|
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`
|
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
|
profile-refresh to the in-app session, a change switches the account's email in place,
|
||||||
interactive flow. The confirm runs on load; the token rides the URL fragment (never
|
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
|
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
|
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
|
**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
|
is revealed **only after** the proof is verified and is performed behind an
|
||||||
explicit, irreversible confirmation. A free identity is simply attached (and a
|
explicit, irreversible confirmation. A free identity is simply attached (and a
|
||||||
guest is promoted to durable, clearing `is_guest`).
|
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**
|
- **Merge** retires the account that owns the linked identity into the **current**
|
||||||
account, in a single transaction (`internal/accountmerge`): statistics summed
|
account, in a single transaction (`internal/accountmerge`): statistics summed
|
||||||
(counters incl. moves/hints added, max points kept, and the per-variant best moves
|
(counters incl. moves/hints added, max points kept, and the per-variant best moves
|
||||||
|
|||||||
+10
-4
@@ -96,10 +96,6 @@ reconnect), and pending reads resume on their own — the interface stays usable
|
|||||||
flashing a red banner each time.
|
flashing a red banner each time.
|
||||||
|
|
||||||
### Accounts, linking & merge
|
### 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
|
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
|
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
|
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
|
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.
|
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
|
### Lobby & matchmaking
|
||||||
On a cold open the lobby greets the player with a brief **loading splash** — Scrabble tiles
|
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
|
spelling **ЭРУДИТ / ЗАГРУЗКА / ОЖИДАНИЕ** as a small crossword — that clears the moment the
|
||||||
|
|||||||
+10
-4
@@ -100,10 +100,6 @@ Telegram держит **единого бота**: все игроки поль
|
|||||||
рабочим вместо красного баннера каждый раз.
|
рабочим вместо красного баннера каждый раз.
|
||||||
|
|
||||||
### Аккаунты, привязка и слияние
|
### Аккаунты, привязка и слияние
|
||||||
_Вход сейчас только через провайдера, поэтому UI привязки в профиле временно скрыт; он
|
|
||||||
вернётся, когда появится анонимный `/app/`-гость (для апгрейда которого он и нужен). Описание
|
|
||||||
ниже — на этот случай._
|
|
||||||
|
|
||||||
Первый контакт с платформы заводит постоянный аккаунт. Из профиля игрок
|
Первый контакт с платформы заводит постоянный аккаунт. Из профиля игрок
|
||||||
привязывает email (по confirm-коду) или свой Telegram (через веб-вход); гость,
|
привязывает email (по confirm-коду) или свой Telegram (через веб-вход); гость,
|
||||||
привязавший первую личность, становится постоянным аккаунтом. Факт «личность уже
|
привязавший первую личность, становится постоянным аккаунтом. Факт «личность уже
|
||||||
@@ -115,6 +111,16 @@ _Вход сейчас только через провайдера, поэто
|
|||||||
тогда сохраняется постоянный аккаунт, а игры гостя переходят в него. Слияние
|
тогда сохраняется постоянный аккаунт, а игры гостя переходят в него. Слияние
|
||||||
запрещено, только пока у аккаунтов есть общая незавершённая игра.
|
запрещено, только пока у аккаунтов есть общая незавершённая игра.
|
||||||
|
|
||||||
|
В профиле перечислены **способы входа** аккаунта. В вебе игрок может добавить
|
||||||
|
Telegram; добавление VK в вебе пока не предлагается, а внутри Mini App платформа-хозяин
|
||||||
|
уже привязана. Привязанного провайдера можно **отвязать** — кроме последнего
|
||||||
|
оставшегося способа входа: он не отвязывается, чтобы аккаунт оставался достижимым.
|
||||||
|
**Email не отвязывают — его меняют**: игрок вводит новый адрес, подтверждает код,
|
||||||
|
отправленный на него, и аккаунт атомарно переключается на новый адрес, освобождая
|
||||||
|
старый. Новый адрес, уже принадлежащий другому аккаунту, отклоняется нейтральным
|
||||||
|
«проверьте правильность e-mail или обратитесь в поддержку» — смена никогда не сливает
|
||||||
|
аккаунты и не раскрывает чужой.
|
||||||
|
|
||||||
### Лобби и подбор
|
### Лобби и подбор
|
||||||
При холодном запуске лобби встречает игрока короткой **заставкой загрузки** — фишки Scrabble
|
При холодном запуске лобби встречает игрока короткой **заставкой загрузки** — фишки Scrabble
|
||||||
складывают небольшой кроссворд из слов **ЭРУДИТ / ЗАГРУЗКА / ОЖИДАНИЕ** — и она исчезает, как
|
складывают небольшой кроссворд из слов **ЭРУДИТ / ЗАГРУЗКА / ОЖИДАНИЕ** — и она исчезает, как
|
||||||
|
|||||||
Reference in New Issue
Block a user