* fix(oauth): stop treating a foreign window.opener as a bind flow
The /oauth/:provider callback decided between an account bind and a plain
login with `window.opener ? 'bind' : 'login'`. Any tab opened from an
external link (target="_blank", Slack, mail clients, another site) carries
a live opener, and that opener survives the cross-origin round trip to the
identity provider. Such a login callback was therefore misread as a bind:
it posted a handshake to a window that speaks no such protocol, showed the
"binding your account" screen, and hung until the 30s deadline fired with
"OAuth binding timed out" — while the backend was never called at all.
Reproduced against a real Keycloak round trip: a tab opened via window.open
still reports window.opener !== null on the callback, so mode resolved to
'bind' for an ordinary OIDC login.
A bind now requires positive proof: the popup we open for it is same-origin
(about:blank) before being sent to the provider, so we stamp its own
sessionStorage. The stamp rides through the provider round trip and is
scoped to that popup alone, so a login tab can never carry it. Ambiguity
resolves to 'login', which is the recoverable direction.
Affects every provider sharing this callback (OIDC, GitHub, Discord,
LinuxDO, custom).
* fix(oauth): harden bind popup detection