238 lines · 8.7 KB · TypeScript
RawDownload
import { createClerkClient, verifyToken } from "@clerk/backend";import { describe, expect, it } from "@effect/vitest";import { vi } from "vite-plus/test";import * as Context from "effect/Context";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as Option from "effect/Option";import * as Predicate from "effect/Predicate";import * as Redacted from "effect/Redacted";import * as Tracer from "effect/Tracer";import * as HttpRouter from "effect/unstable/http/HttpRouter";import * as HttpServerRequest from "effect/unstable/http/HttpServerRequest";import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse";import { RelayEnvironmentAuth } from "@t3tools/contracts/relay"; import {  relayCors,  relayDocsRedirectRoute,  relayEnvironmentAuthLayer,  relayNotFoundRoute,  traceRelayHttpRequestWith,  verifyRelayClientBearerToken,  withoutCapturedParentSpan,} from "./Api.ts";import * as RelayConfiguration from "../Config.ts";import * as EnvironmentCredentials from "../environments/EnvironmentCredentials.ts"; vi.mock("@clerk/backend", () => ({  createClerkClient: vi.fn(),  verifyToken: vi.fn(),})); const relaySettings: RelayConfiguration.RelayConfiguration["Service"] = {  relayIssuer: "https://relay.example.test",  apns: {    teamId: "apns-team",    keyId: "apns-key",    privateKey: Redacted.make("apns-private-key"),    bundleId: "com.example.t3",    environment: "sandbox",  },  clerkSecretKey: Redacted.make("clerk-secret-key"),  clerkPublishableKey: "pk_test_test",  clerkJwtAudience: "t3-code-relay",  apnsDeliveryJobSigningSecret: Redacted.make("apns-delivery-secret"),  cloudMintPrivateKey: Redacted.make("cloud-mint-private-key"),  cloudMintPublicKey: "cloud-mint-public-key",  managedEndpointBaseDomain: undefined,  managedEndpointNamespace: undefined,}; describe("relay client authentication", () => {  it.effect("preserves the existing Clerk session JWT path", () =>    Effect.gen(function* () {      vi.mocked(verifyToken).mockResolvedValue({        sub: "user_session",        aud: relaySettings.clerkJwtAudience,      } as never);       expect(yield* verifyRelayClientBearerToken(relaySettings, "session-token")).toEqual({        sub: "user_session",        mode: "clerk_session_bearer",      });      expect(verifyToken).toHaveBeenCalledWith("session-token", {        secretKey: "clerk-secret-key",        audience: relaySettings.clerkJwtAudience,      });      expect(createClerkClient).not.toHaveBeenCalled();    }).pipe(      Effect.ensuring(        Effect.sync(() => {          vi.mocked(verifyToken).mockReset();          vi.mocked(createClerkClient).mockReset();        }),      ),    ),  );   it.effect("falls back to Clerk OAuth token verification for the headless CLI", () =>    Effect.gen(function* () {      vi.mocked(verifyToken).mockRejectedValue(new Error("not a session JWT"));      vi.mocked(createClerkClient).mockReturnValue({        authenticateRequest: vi.fn().mockResolvedValue({          isAuthenticated: true,          toAuth: () => ({ userId: "user_oauth" }),        }),      } as never);       expect(yield* verifyRelayClientBearerToken(relaySettings, "oauth-token")).toEqual({        sub: "user_oauth",        mode: "clerk_oauth_bearer",      });      expect(createClerkClient).toHaveBeenCalledWith({        secretKey: "clerk-secret-key",        publishableKey: "pk_test_test",      });    }).pipe(      Effect.ensuring(        Effect.sync(() => {          vi.mocked(verifyToken).mockReset();          vi.mocked(createClerkClient).mockReset();        }),      ),    ),  );}); describe("relay environment authentication", () => {  it.effect("preserves credential lookup persistence failures as internal errors", () => {    const failure = new EnvironmentCredentials.EnvironmentCredentialAuthenticatePersistenceError({      cause: "database unavailable",    });    const credentials: EnvironmentCredentials.EnvironmentCredentials["Service"] = {      create: () => Effect.die("unused create"),      authenticate: () => Effect.fail(failure),      revokeForEnvironmentPublicKey: () => Effect.die("unused revoke"),    };     return Effect.gen(function* () {      const auth = yield* RelayEnvironmentAuth;      const error = yield* Effect.flip(        auth.environmentBearer(Effect.succeed(HttpServerResponse.empty()), {          credential: Redacted.make("environment-credential"),          endpoint: {} as never,          group: {} as never,        }),      );       expect(Predicate.isTagged(error, "RelayInternalError")).toBe(true);      if (Predicate.isTagged(error, "RelayInternalError")) {        expect(error.reason).toBe("persistence_failed");      }    }).pipe(      Effect.provideService(        HttpServerRequest.HttpServerRequest,        HttpServerRequest.fromWeb(new Request("https://relay.test/v1/server/link")),      ),      Effect.provideService(HttpServerRequest.ParsedSearchParams, {}),      Effect.provideService(HttpRouter.RouteContext, {        params: {},        route: {} as never,      }),      Effect.provide(        relayEnvironmentAuthLayer.pipe(          Layer.provide(Layer.succeed(EnvironmentCredentials.EnvironmentCredentials, credentials)),        ),      ),      Effect.scoped,    );  });}); describe("relay request tracing", () => {  it.effect(    "does not parent endpoint spans to an ambient parent captured while building handlers",    () =>      Effect.gen(function* () {        const spans: Array<Tracer.NativeSpan> = [];        const tracer = Tracer.make({          span: (options) => {            const span = new Tracer.NativeSpan(options);            spans.push(span);            return span;          },        });        const ambientParent = Tracer.externalSpan({          traceId: "00000000000000000000000000000001",          spanId: "0000000000000001",          sampled: true,        });        const endpoint = yield* withoutCapturedParentSpan(          Effect.context<never>().pipe(            Effect.map((capturedContext: Context.Context<never>) =>              Effect.succeed(HttpServerResponse.empty({ status: 204 })).pipe(                Effect.withSpan("relay.test.endpoint"),                Effect.provideContext(capturedContext),              ),            ),          ),        ).pipe(Effect.provideService(Tracer.ParentSpan, ambientParent));        const request = HttpServerRequest.fromWeb(          new Request("https://relay.test/v1/mobile/devices?client=mobile", {            method: "POST",            headers: {              authorization: "Bearer secret",              dpop: "signed-proof",            },          }),        );         yield* traceRelayHttpRequestWith(endpoint, Layer.succeed(Tracer.Tracer, tracer)).pipe(          Effect.provideService(HttpServerRequest.HttpServerRequest, request),        );         expect(spans.map((span) => span.name)).toEqual(["http.server POST", "relay.test.endpoint"]);        expect(spans[0]?.kind).toBe("server");        expect(spans[0]?.attributes.get("url.path")).toBe("/v1/mobile/devices");        expect(spans[0]?.attributes.get("http.response.status_code")).toBe(204);        expect(spans[0]?.attributes.get("http.request.header.authorization")).toBe("<redacted>");        expect(spans[0]?.attributes.get("http.request.header.dpop")).toBe("<redacted>");        expect(Option.isNone(spans[0]!.parent)).toBe(true);        expect(Option.getOrUndefined(spans[1]!.parent)?.spanId).toBe(spans[0]?.spanId);      }),  );}); describe("relay routing fallback", () => {  it.effect("redirects the relay root to the API docs", () =>    Effect.gen(function* () {      const request = HttpServerRequest.fromWeb(new Request("https://relay.test/"));      const httpEffect = yield* HttpRouter.toHttpEffect(        Layer.mergeAll(relayDocsRedirectRoute, relayNotFoundRoute, relayCors),      );      const response = yield* httpEffect.pipe(        Effect.provideService(HttpServerRequest.HttpServerRequest, request),      );       expect(response.status).toBe(302);      expect(response.headers.location).toBe("/docs");      expect(response.headers["access-control-allow-origin"]).toBe("*");    }).pipe(Effect.scoped),  );   it.effect("returns a CORS-compatible 404 response for unmatched paths", () =>    Effect.gen(function* () {      const request = HttpServerRequest.fromWeb(        new Request("https://relay.test/v1/environmentsd", { method: "GET" }),      );      const httpEffect = yield* HttpRouter.toHttpEffect(Layer.merge(relayNotFoundRoute, relayCors));      const response = yield* httpEffect.pipe(        Effect.provideService(HttpServerRequest.HttpServerRequest, request),      );       expect(response.status).toBe(404);      expect(response.headers["access-control-allow-origin"]).toBe("*");    }).pipe(Effect.scoped),  );});