1// @effect-diagnostics-next-line nodeBuiltinImport:off -- Older desktops derive the token with Node's HMAC.
2import * as NodeCrypto from "node:crypto";
3
4import * as PlatformNodeCrypto from "@effect/platform-node/NodeCrypto";
5import { describe, expect, it } from "@effect/vitest";
6import * as Effect from "effect/Effect";
7
8import {
9 DESKTOP_BOOTSTRAP_TOKEN_WINDOW_MS,
10 currentDesktopBootstrapToken,
11 isValidDesktopBootstrapToken,
12} from "./desktopBootstrapToken.ts";
13
14const SECRET = "desktop-secret";
15const WINDOW = DESKTOP_BOOTSTRAP_TOKEN_WINDOW_MS;
16
17describe("desktop bootstrap token", () => {
18 it.effect("rotates every window and stays accepted for one more", () =>
19 Effect.gen(function* () {
20 const issuedAt = 10 * WINDOW + 1;
21 const token = yield* currentDesktopBootstrapToken(SECRET, issuedAt);
22
23 expect(token).toBe(
24 NodeCrypto.createHmac("sha256", SECRET).update("t3-desktop-bootstrap:10").digest("hex"),
25 );
26 expect(yield* currentDesktopBootstrapToken(SECRET, issuedAt + WINDOW)).not.toBe(token);
27 expect(yield* isValidDesktopBootstrapToken(SECRET, token, issuedAt)).toBe(true);
28 expect(yield* isValidDesktopBootstrapToken(SECRET, token, issuedAt + WINDOW)).toBe(true);
29 expect(yield* isValidDesktopBootstrapToken(SECRET, token, issuedAt + 2 * WINDOW)).toBe(false);
30 }).pipe(Effect.provide(PlatformNodeCrypto.layerMac)),
31 );
32
33 it.effect("accepts a token from a desktop clock slightly ahead of the backend", () =>
34 Effect.gen(function* () {
35 const desktopNow = 10 * WINDOW + 500;
36 const backendNow = 10 * WINDOW - 500;
37 const ahead = yield* currentDesktopBootstrapToken(SECRET, desktopNow);
38 const tooFarAhead = yield* currentDesktopBootstrapToken(SECRET, desktopNow + WINDOW);
39
40 expect(yield* isValidDesktopBootstrapToken(SECRET, ahead, backendNow)).toBe(true);
41 expect(yield* isValidDesktopBootstrapToken(SECRET, tooFarAhead, backendNow)).toBe(false);
42 }).pipe(Effect.provide(PlatformNodeCrypto.layerMac)),
43 );
44
45 it.effect("rejects tokens derived from another secret", () =>
46 Effect.gen(function* () {
47 const token = yield* currentDesktopBootstrapToken("other-secret", WINDOW);
48
49 expect(yield* isValidDesktopBootstrapToken(SECRET, token, WINDOW)).toBe(false);
50 expect(yield* isValidDesktopBootstrapToken(SECRET, "short", WINDOW)).toBe(false);
51 }).pipe(Effect.provide(PlatformNodeCrypto.layerMac)),
52 );
53});