46 lines
4.4 KiB
Markdown
46 lines
4.4 KiB
Markdown
---
|
|
type: knowledge
|
|
summary: The sign-in flow end to end, and which layer owns each step — in particular that the broker redirect belongs to @ng-org/web, not to this package
|
|
---
|
|
|
|
# How a user gets in
|
|
|
|
## The steps, and who owns them
|
|
|
|
A newcomer holds nothing. To act they need a wallet imported once into the wallet application, and an identifier naming their virtual space. The `barrier` hands out the first three — the wallet file, its password, a link to the wallet application — and takes the fourth.
|
|
|
|
Then the page is handed to the broker, which opens the wallet and reloads the application **inside an iframe it hosts**. Outside that iframe there is no session.
|
|
|
|
**The redirect is not ours.** `@ng-org/web`'s `init()` performs it as its first statement — same host, same URL shape, same frame check. Verified in its shipped bundle: when `window.self === window.top` it assigns `window.location.href` from a hard-coded `redirect` constant. This package must not reimplement it; doing so would double a function of the layer below and diverge from it in the meantime (see `.project/rules/nextgraph/no-divergence-from-nextgraph.md`).
|
|
|
|
What is genuinely ours is the one thing `init()` cannot do: **put the identifier into the URL before `init()` reads it**. `init()` hands over `window.location.href`; it does not know the parameter exists.
|
|
|
|
## When the `barrier` shows, and why it is not a question of identity
|
|
|
|
**Top-level: always. Inside the broker iframe: never** — there the identifier arrives in the URL and the page stands aside.
|
|
|
|
The discriminator is the frame, not whether an identity is known, and the difference is not ergonomic. An identity is state we can observe; whether the person still has a wallet in this browser is state we cannot — it lives in another origin's storage. A screen that decides on the identity is therefore *guessing* at the state that matters, and when it guesses "already set up" while NextGraph's side is gone, it hides the only controls that could repair the situation.
|
|
|
|
What that costs is not a degraded experience but a dead end: sent to the broker without a wallet, the person lands on a static page carrying **no return path to the application**. The browser's Back button is the only way out, and it only helps if the `barrier` is there to catch them on arrival.
|
|
|
|
A known identifier therefore **prefills the field; it never skips the screen**. And nothing detects whether the wallet is already there: all steps are always shown, and whoever already has one ignores them. No checkbox, no "I already have it" — knowing whether you imported a wallet into this browser is not something a person can be asked.
|
|
|
|
Two facts, observed on the live sites, close the alternatives: clicking the wallet application's import control opens a file chooser **in place** — no navigation, no new tab; and our own page receives **no signal at all** when the import succeeds (no opener, no message, no storage or focus event, no reload). Detecting the return is impossible, not merely fragile.
|
|
|
|
## Why the identifier travels by URL
|
|
|
|
The flow runs in **two contexts with separate storage partitions** — the top-level page and the broker iframe — because browsers partition storage by top-level site. A value written top-level is not the value the iframe reads.
|
|
|
|
What crosses is the URL: the redirect embeds the full application URL, query included, and reloads it in the iframe. Hence the resolution order, which must not be "simplified":
|
|
|
|
1. the identifier in the URL — it wins whenever present, being the only thing that crosses;
|
|
2. otherwise storage — same-partition convenience, and prefill on reload.
|
|
|
|
The parameter must therefore be written on **every** path that settles an identity, not only when the `barrier` asked. An identity coming from storage without the parameter being written is the silent failure: the iframe reads an empty identity, provisions a second virtual space, and the returning user lands somewhere empty with no error.
|
|
|
|
Writing to storage and writing to the address bar are two independent attempts, deliberately: a storage that refuses writes must not cost the round-trip its parameter.
|
|
|
|
## What survives migration
|
|
|
|
The `barrier` evaporates whole — handing out a shared wallet and naming a space exist only because the wallet is shared. The redirect does **not**: upstream's real multi-wallet flow has the same shape, and the barrier becomes the real per-user sign-in. The call site survives too, which is why the published call takes no identifier.
|