342 lines · 11.4 KB · TypeScript
RawDownload
import { AuthStandardClientScopes, EnvironmentId } from "@t3tools/contracts";import { describe, expect, it } from "@effect/vitest";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as Option from "effect/Option";import * as Ref from "effect/Ref"; import * as ManagedRelay from "../relay/managedRelay.ts";import { remoteHttpClientLayer } from "../rpc/http.ts";import * as ClientCapabilities from "../platform/capabilities.ts";import * as RemoteEnvironmentAuthorization from "./service.ts";import * as TokenStore from "./tokenStore.ts"; const ENVIRONMENT_ID = EnvironmentId.make("environment-1");const ENDPOINT = {  httpBaseUrl: "https://environment.example.test",  wsBaseUrl: "wss://environment.example.test",  providerKind: "cloudflare_tunnel" as const,};const DESCRIPTOR = {  environmentId: ENVIRONMENT_ID,  label: "Remote environment",  platform: {    os: "linux",    arch: "x64",  },  serverVersion: "0.0.0-test",  capabilities: {    repositoryIdentity: true,  },};const BOOTSTRAP: RemoteEnvironmentAuthorization.RelayEnvironmentAuthorization = {  environmentId: ENVIRONMENT_ID,  endpoint: ENDPOINT,  credential: "relay-bootstrap",}; function recordedFetch(responses: ReadonlyArray<Response>) {  const calls: Array<readonly [RequestInfo | URL, RequestInit]> = [];  let responseIndex = 0;  const fetchFn = ((input, init) => {    calls.push([input, init ?? {}]);    const response = responses[responseIndex++];    return response === undefined      ? Promise.reject(new Error(`Unexpected fetch call to ${String(input)}`))      : Promise.resolve(response);  }) satisfies typeof fetch;  return { calls, fetchFn };} const websocketTicket = (ticket: string) =>  Response.json({    ticket,    expiresAt: "2026-06-06T01:00:00.000Z",  }); const accessToken = (token: string) =>  Response.json({    access_token: token,    issued_token_type: "urn:ietf:params:oauth:token-type:access_token",    token_type: "DPoP",    expires_in: 3_600,    scope: AuthStandardClientScopes.join(" "),  }); const authInvalid = () =>  Response.json(    {      _tag: "EnvironmentAuthInvalidError",      code: "auth_invalid",      reason: "invalid_credential",      traceId: "trace-auth-invalid",    },    { status: 401 },  ); const makeHarness = Effect.fn("TestRemoteAuthorization.makeHarness")(function* (input: {  readonly initialToken?: TokenStore.RemoteDpopAccessToken;  readonly responses: ReadonlyArray<Response>;}) {  const tokens = yield* Ref.make(    new Map(      input.initialToken === undefined        ? []        : [[input.initialToken.environmentId, input.initialToken]],    ),  );  const bootstrapCalls = yield* Ref.make(0);  const proofInputs = yield* Ref.make<    ReadonlyArray<{      readonly method: string;      readonly url: string;      readonly accessToken?: string;    }>  >([]);  const fetch = recordedFetch(input.responses);   const tokenStore = TokenStore.RemoteDpopAccessTokenStore.of({    get: (environmentId) =>      Ref.get(tokens).pipe(        Effect.map((current) => Option.fromUndefinedOr(current.get(environmentId))),      ),    put: (token) =>      Ref.update(tokens, (current) => {        const next = new Map(current);        next.set(token.environmentId, token);        return next;      }),    remove: (environmentId) =>      Ref.update(tokens, (current) => {        const next = new Map(current);        next.delete(environmentId);        return next;      }),  });  const signer = ManagedRelay.ManagedRelayDpopSigner.of({    thumbprint: Effect.succeed("thumbprint-1"),    createProof: (proofInput) =>      Ref.update(proofInputs, (current) => [...current, proofInput]).pipe(        Effect.as(`proof:${proofInput.url}`),      ),  });  const layer = RemoteEnvironmentAuthorization.layer.pipe(    Layer.provide(      Layer.mergeAll(        remoteHttpClientLayer(fetch.fetchFn),        Layer.succeed(ManagedRelay.ManagedRelayDpopSigner, signer),        Layer.succeed(TokenStore.RemoteDpopAccessTokenStore, tokenStore),        Layer.succeed(          ClientCapabilities.ClientPresentation,          ClientCapabilities.ClientPresentation.of({            metadata: {              label: "T3 Code Test",              deviceType: "mobile",              os: "test",            },            scopes: AuthStandardClientScopes,          }),        ),      ),    ),  );  const obtainBootstrap = Ref.update(bootstrapCalls, (count) => count + 1).pipe(    Effect.as(BOOTSTRAP),  );   return {    layer,    tokens,    bootstrapCalls,    proofInputs,    fetch,    obtainBootstrap,  };}); describe("RemoteEnvironmentAuthorization", () => {  it.effect("reuses a valid persisted environment token without contacting the relay", () =>    Effect.gen(function* () {      const cached = new TokenStore.RemoteDpopAccessToken({        environmentId: ENVIRONMENT_ID,        label: DESCRIPTOR.label,        endpoint: ENDPOINT,        accessToken: "cached-access-token",        expiresAtEpochMs: Number.MAX_SAFE_INTEGER,        dpopThumbprint: "thumbprint-1",      });      const harness = yield* makeHarness({        initialToken: cached,        responses: [websocketTicket("cached-ticket")],      });       const authorized = yield* Effect.gen(function* () {        const remote = yield* RemoteEnvironmentAuthorization.RemoteEnvironmentAuthorization;        return yield* remote.authorizeDpop({          expectedEnvironmentId: ENVIRONMENT_ID,          obtainBootstrap: harness.obtainBootstrap,        });      }).pipe(Effect.provide(harness.layer));       expect(authorized.socketUrl).toContain("wsTicket=cached-ticket");      expect(yield* Ref.get(harness.bootstrapCalls)).toBe(0);      expect(harness.fetch.calls).toHaveLength(1);      expect(String(harness.fetch.calls[0]?.[0])).toBe(        "https://environment.example.test/api/auth/websocket-ticket",      );    }),  );   it.effect("refreshes and persists an expired environment token", () =>    Effect.gen(function* () {      const expired = new TokenStore.RemoteDpopAccessToken({        environmentId: ENVIRONMENT_ID,        label: DESCRIPTOR.label,        endpoint: ENDPOINT,        accessToken: "expired-access-token",        expiresAtEpochMs: 0,        dpopThumbprint: "thumbprint-1",      });      const harness = yield* makeHarness({        initialToken: expired,        responses: [          Response.json(DESCRIPTOR),          accessToken("fresh-access-token"),          websocketTicket("fresh-ticket"),        ],      });       const authorized = yield* Effect.gen(function* () {        const remote = yield* RemoteEnvironmentAuthorization.RemoteEnvironmentAuthorization;        return yield* remote.authorizeDpop({          expectedEnvironmentId: ENVIRONMENT_ID,          obtainBootstrap: harness.obtainBootstrap,        });      }).pipe(Effect.provide(harness.layer));       expect(authorized.socketUrl).toContain("wsTicket=fresh-ticket");      expect(yield* Ref.get(harness.bootstrapCalls)).toBe(1);      expect((yield* Ref.get(harness.tokens)).get(ENVIRONMENT_ID)).toEqual(        expect.objectContaining({          accessToken: "fresh-access-token",          dpopThumbprint: "thumbprint-1",        }),      );      expect(harness.fetch.calls).toHaveLength(3);    }),  );   it.effect("evicts an auth-invalid cached token and obtains a fresh bootstrap", () =>    Effect.gen(function* () {      const cached = new TokenStore.RemoteDpopAccessToken({        environmentId: ENVIRONMENT_ID,        label: DESCRIPTOR.label,        endpoint: ENDPOINT,        accessToken: "invalid-access-token",        expiresAtEpochMs: Number.MAX_SAFE_INTEGER,        dpopThumbprint: "thumbprint-1",      });      const harness = yield* makeHarness({        initialToken: cached,        responses: [          authInvalid(),          Response.json(DESCRIPTOR),          accessToken("replacement-access-token"),          websocketTicket("replacement-ticket"),        ],      });       const authorized = yield* Effect.gen(function* () {        const remote = yield* RemoteEnvironmentAuthorization.RemoteEnvironmentAuthorization;        return yield* remote.authorizeDpop({          expectedEnvironmentId: ENVIRONMENT_ID,          obtainBootstrap: harness.obtainBootstrap,        });      }).pipe(Effect.provide(harness.layer));       expect(authorized.socketUrl).toContain("wsTicket=replacement-ticket");      expect(yield* Ref.get(harness.bootstrapCalls)).toBe(1);      expect((yield* Ref.get(harness.tokens)).get(ENVIRONMENT_ID)).toEqual(        expect.objectContaining({          accessToken: "replacement-access-token",        }),      );      expect(harness.fetch.calls).toHaveLength(4);    }),  );   it.effect("refreshes a cached endpoint after consecutive transient failures", () =>    Effect.gen(function* () {      const cached = new TokenStore.RemoteDpopAccessToken({        environmentId: ENVIRONMENT_ID,        label: DESCRIPTOR.label,        endpoint: ENDPOINT,        accessToken: "cached-access-token",        expiresAtEpochMs: Number.MAX_SAFE_INTEGER,        dpopThumbprint: "thumbprint-1",      });      const harness = yield* makeHarness({        initialToken: cached,        responses: [          new Response("endpoint unavailable", { status: 503 }),          new Response("endpoint still unavailable", { status: 503 }),          Response.json(DESCRIPTOR),          accessToken("replacement-access-token"),          websocketTicket("replacement-ticket"),        ],      });       const authorized = yield* Effect.gen(function* () {        const remote = yield* RemoteEnvironmentAuthorization.RemoteEnvironmentAuthorization;        const firstFailure = yield* remote          .authorizeDpop({            expectedEnvironmentId: ENVIRONMENT_ID,            obtainBootstrap: harness.obtainBootstrap,          })          .pipe(Effect.flip);         expect(firstFailure._tag).toBe("ConnectionTransientError");        expect(yield* Ref.get(harness.bootstrapCalls)).toBe(0);        expect((yield* Ref.get(harness.tokens)).get(ENVIRONMENT_ID)).toBe(cached);         return yield* remote.authorizeDpop({          expectedEnvironmentId: ENVIRONMENT_ID,          obtainBootstrap: harness.obtainBootstrap,        });      }).pipe(Effect.provide(harness.layer));       expect(authorized.socketUrl).toContain("wsTicket=replacement-ticket");      expect(yield* Ref.get(harness.bootstrapCalls)).toBe(1);      expect((yield* Ref.get(harness.tokens)).get(ENVIRONMENT_ID)).toEqual(        expect.objectContaining({          accessToken: "replacement-access-token",        }),      );      expect(harness.fetch.calls).toHaveLength(5);    }),  );   it.effect("does not persist a refreshed token until its websocket ticket succeeds", () =>    Effect.gen(function* () {      const harness = yield* makeHarness({        responses: [          Response.json(DESCRIPTOR),          accessToken("unusable-access-token"),          new Response("endpoint unavailable", { status: 503 }),        ],      });       yield* Effect.gen(function* () {        const remote = yield* RemoteEnvironmentAuthorization.RemoteEnvironmentAuthorization;        return yield* remote.authorizeDpop({          expectedEnvironmentId: ENVIRONMENT_ID,          obtainBootstrap: harness.obtainBootstrap,        });      }).pipe(Effect.provide(harness.layer), Effect.flip);       expect((yield* Ref.get(harness.tokens)).has(ENVIRONMENT_ID)).toBe(false);      expect(yield* Ref.get(harness.bootstrapCalls)).toBe(1);      expect(harness.fetch.calls).toHaveLength(3);    }),  );});