feat(xiaomi-mimo): server-assisted desktop login, five account clusters, v2.6 models
Reproduce the MiMo Desktop login surface server-side so headless/Docker deployments can link a Xiaomi account without the Desktop client. The account session (passToken) is captured during the proxied login and stored per connection. - Five account clusters (cn/sgp/ams/ru/in): per-region mimo-server host and SSO sid, unknown region falls back to sgp - mimo-v2.6-pro/flash/pro-ultraspeed dual-route models: account-service route when desktop credentials exist, cloud API (sk- key) otherwise; drops obsolete mimo-x-*-preview ids - Desktop ServiceTokenManager 2-phase handshake (single serviceLogin with target sid, raw 64-bit nonce preserved), per-region session cache - reasoning_effort bridged to output_config.effort; i18n runtime now observes characterData mutations so React text rewrites get translated - Security hardening on the login proxy: session travels only in the httpOnly cookie (never in the URL), proxy branch requires dashboard auth, authorization/proxy-authorization never forwarded upstream, and upstream Set-Cookie is not replayed onto the app origin
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Security invariants of the server-assisted MiMo login proxy
|
||||
* (src/lib/mimoLoginSession.js):
|
||||
* - credentials bound to 9router's own origin are never forwarded upstream
|
||||
* - upstream Set-Cookie is never replayed onto the app's own cookie jar
|
||||
*/
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { __test__ } from "../../src/lib/mimoLoginSession.js";
|
||||
|
||||
const { STRIP_UPSTREAM_HEADERS, buildBrowserResponse } = __test__;
|
||||
|
||||
describe("mimo login proxy security", () => {
|
||||
it("strips auth credentials and session cookies before forwarding upstream", () => {
|
||||
for (const h of ["authorization", "proxy-authorization", "cookie", "host"]) {
|
||||
expect(STRIP_UPSTREAM_HEADERS.has(h)).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it("does not replay upstream Set-Cookie onto the app origin", async () => {
|
||||
const upstream = new Response("ok", {
|
||||
status: 200,
|
||||
headers: {
|
||||
"content-type": "text/html",
|
||||
"set-cookie": "userId=123; Path=/", // plain object header: visible via getSetCookie
|
||||
},
|
||||
});
|
||||
const out = await buildBrowserResponse({ jar: new Map() }, upstream, "http://localhost:20128", "/pass/");
|
||||
expect(out.headers.getSetCookie()).toEqual([]);
|
||||
});
|
||||
|
||||
it("keeps ordinary response headers intact", async () => {
|
||||
const upstream = new Response("<html></html>", {
|
||||
status: 200,
|
||||
headers: { "content-type": "text/html" },
|
||||
});
|
||||
const out = await buildBrowserResponse({ jar: new Map() }, upstream, "http://localhost:20128", "/fe/");
|
||||
expect(out.status).toBe(200);
|
||||
expect(out.headers.get("content-type")).toBe("text/html");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user