+
${translateText("session_expired.body")}
+
+
+
+
+
+ `;
+ }
+}
diff --git a/tests/client/Auth.test.ts b/tests/client/Auth.test.ts
new file mode 100644
index 0000000000..74fca278b3
--- /dev/null
+++ b/tests/client/Auth.test.ts
@@ -0,0 +1,136 @@
+import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
+
+// Auth.ts derives the API origin from window.location via ./Api. Pin it so the
+// fetch URLs are deterministic in the jsdom environment.
+vi.mock("../../src/client/Api", () => ({
+ getApiBase: () => "http://localhost:8787",
+ getAudience: () => "localhost",
+}));
+
+import { getLastRefreshOutcome, logOut, userAuth } from "../../src/client/Auth";
+
+const PERSISTENT_ID_KEY = "player_persistent_id";
+
+// Build a decodeable JWT whose `iss` matches getApiBase() so userAuth()'s
+// claim checks pass. Only the payload matters to decodeJwt.
+function fakeJwt(payload: Record