Phase 5: [link account linking
BridgeAccountLink ties a game account to a website account. [link mints a one-time, 5-minute code from an unambiguous alphabet (no O/0/I/1), holds it in a Core-thread dict keyed to the account, and emits link.request. The website relays the code back through the sidecar as link.confirm; the shard validates, writes the WebsiteUserId account tag, and replies link.ok. A bad or expired code gets link.error. The tag persists to accounts.xml in ServUO's standard <tags> format, read by LoadTags at boot, so a link survives restarts with no new persistence layer. mob.login now carries webId when the account is linked, so the sidecar can attribute a session to a site user without a lookup. Safeguards: one-time codes; only the newest code per account is valid; per-account 30s rate limit against code spam; a 1-minute purge bounds the code table; the websiteUserId is trusted only because the socket is loopback-only. The tag reaches memory on confirm but disk only on the next save — a hard crash between loses it, and the player just re-runs [link. Verified end to end with a smart stub that reads the emitted code and confirms it: link.request -> link.confirm -> link.ok, a bad code -> link.error, and the tag observed in accounts.xml after a save. Evidence in docs/PLAN.md §15. The [link command body is exposed as RequestLink(Mobile) so it can be driven in tests without a client. Adds tools/stub_sidecar_link.ps1 and tools/scaffolding/BridgeLinkProbe.cs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -123,11 +123,19 @@ namespace Server.Custom.Bridge
|
||||
if (m == null)
|
||||
return;
|
||||
|
||||
BridgeLink.Emit(BridgeJson.Begin("mob.login")
|
||||
// Carry the linked website id on the login anchor so the sidecar can attribute
|
||||
// this session (and everything after it) to a site user without a lookup.
|
||||
var webId = BridgeAccountLink.WebIdFor(m.Account as Account);
|
||||
|
||||
var sb = BridgeJson.Begin("mob.login")
|
||||
.Mob("who", m)
|
||||
.Str("map", m.Map == null ? null : m.Map.Name)
|
||||
.Num("x", m.X).Num("y", m.Y).Num("z", m.Z)
|
||||
.End());
|
||||
.Num("x", m.X).Num("y", m.Y).Num("z", m.Z);
|
||||
|
||||
if (webId != null)
|
||||
sb.Str("webId", webId);
|
||||
|
||||
BridgeLink.Emit(sb.End());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user