fix(auth): add Windows session storage bridge

This commit is contained in:
2026-08-16 21:43:03 +02:00
parent 6bf8218fea
commit c9b02fc57e
14 changed files with 1630 additions and 8 deletions
+79 -2
View File
@@ -488,20 +488,97 @@ describe("file-backed auth session store", () => {
await expectStoreInvalid(store.revoke(created.token));
});
test("rejects native Windows storage before any state write", async () => {
test("routes native Windows session creation through an injected storage bridge", async () => {
const storageRoot = root();
const store = validStore(join(storageRoot, "windows-auth-state"));
const createRecord = vi.fn(async () => true);
const originalPlatform = Object.getOwnPropertyDescriptor(process, "platform");
if (!originalPlatform) throw new Error("platform descriptor unavailable");
Object.defineProperty(process, "platform", { configurable: true, value: "win32" });
try {
await expectStoreInvalid(create(store));
const created = await create(
createFileAuthSessionStore(join(storageRoot, "windows-auth-state"), {
currentAuthConfigRevision: () => revision,
findLocalUser: async () => validLocalUser,
}, { windowsStorageBridge: { create: createRecord } } as never),
);
expect(created.token).toMatch(/^[A-Za-z0-9_-]{43}$/);
expect(createRecord).toHaveBeenCalledTimes(1);
expect(existsSync(join(storageRoot, "windows-auth-state"))).toBe(false);
} finally {
Object.defineProperty(process, "platform", originalPlatform);
}
});
test("routes native Windows lifecycle and OIDC operations only through the storage bridge", async () => {
const records = new Map<string, Buffer>();
const calls: string[] = [];
const key = (directory: string, filename: string) => `${directory}/${filename}`;
const bridge = {
create: async (_root: string, directory: string, filename: string, contents: Buffer) => {
calls.push("create");
const entry = key(directory, filename);
if (records.has(entry)) return false;
records.set(entry, Buffer.from(contents));
return true;
},
read: async (_root: string, directory: string, filename: string) => {
calls.push("read");
const value = records.get(key(directory, filename));
return value === undefined ? undefined : Buffer.from(value);
},
replace: async (_root: string, directory: string, filename: string, contents: Buffer) => {
calls.push("replace");
records.set(key(directory, filename), Buffer.from(contents));
},
remove: async (_root: string, directory: string, filename: string) => {
calls.push("remove");
return records.delete(key(directory, filename));
},
list: async (_root: string, directory: string) => {
calls.push("list");
return [...records.keys()]
.filter((entry) => entry.startsWith(`${directory}/`))
.map((entry) => ({ name: entry.slice(directory.length + 1), modifiedUnixMs: base.getTime() }));
},
claimConsume: async (_root: string, filename: string) => {
calls.push("claim-consume");
const entry = key("oidc", filename);
const value = records.get(entry);
records.delete(entry);
return value === undefined ? undefined : Buffer.from(value);
},
readClaim: async () => undefined,
removeClaim: async () => false,
};
const originalPlatform = Object.getOwnPropertyDescriptor(process, "platform");
if (!originalPlatform) throw new Error("platform descriptor unavailable");
Object.defineProperty(process, "platform", { configurable: true, value: "win32" });
try {
const store = createFileAuthSessionStore("C:\\ProgramData\\ThothII\\auth", {
currentAuthConfigRevision: () => revision,
findLocalUser: async () => validLocalUser,
}, { windowsStorageBridge: bridge } as never);
const session = await create(store);
await expect(store.resolve(session.token)).resolves.toMatchObject({ subject: session.record.subject });
await store.touch(session.token, new Date(base.getTime() + 5 * 60_000));
await store.revoke(session.token);
await expect(store.resolve(session.token)).resolves.toBeUndefined();
const oidc = await store.createOidcState({ nonce: "n".repeat(43), codeVerifier: "v".repeat(43), returnTo: "/" }, base);
await expect(store.consumeOidcState(oidc.state, new Date(base.getTime() + 9 * 60_000)))
.resolves.toMatchObject({ nonce: "n".repeat(43) });
await expect(store.consumeOidcState(oidc.state)).resolves.toBeUndefined();
await create(store, { idleTtlMs: 60_000, absoluteTtlMs: 60_000 });
await store.createOidcState({ nonce: "x".repeat(43), codeVerifier: "y".repeat(43), returnTo: "/" }, base);
await expect(store.prune(new Date(base.getTime() + 11 * 60_000))).resolves.toBe(2);
expect(calls).toEqual(expect.arrayContaining(["create", "read", "replace", "remove", "claim-consume", "list"]));
expect(records).toHaveLength(0);
} finally {
Object.defineProperty(process, "platform", originalPlatform);
}
});
test("revokes on config, local-user, revision, enabled, or role mismatch before returning", async () => {
const storageRoot = root();
let currentRevision = revision;