Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/cloud/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"autumn-js": "^1.2.8",
"drizzle-orm": "catalog:",
"effect": "catalog:",
"iron-webcrypto": "2.0.0",
"jose": "^5.6.3",
"postgres": "^3.4.9",
"posthog-js": "^1.372.5",
Expand Down
283 changes: 282 additions & 1 deletion apps/cloud/src/auth/workos.node.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,287 @@
import { createServer, type IncomingMessage, type ServerResponse } from "node:http";
import type { AddressInfo } from "node:net";

import { describe, expect, it } from "@effect/vitest";
import { env } from "cloudflare:workers";
import { Effect, Schema } from "effect";
import { defaults as ironDefaults, seal as sealIron } from "iron-webcrypto";
import {
SignJWT,
exportJWK,
generateKeyPair,
type JSONWebKeySet,
type JWK,
type KeyLike,
} from "jose";

import { WorkOSClient, collectRawWorkOSList, collectWorkOSList } from "./workos";

const COOKIE_PASSWORD = "test_cookie_password_at_least_32_chars!";
const CLIENT_ID = "client_test";
const API_KEY = "sk_test";
const USER = {
id: "user_test",
email: "test@example.com",
firstName: "Test",
lastName: "User",
profilePictureUrl: "https://example.com/avatar.png",
} as const;

interface Keypair {
readonly kid: string;
readonly publicJwk: JWK;
readonly privateKey: KeyLike;
}

interface RecordedRequest {
readonly method: string;
readonly path: string;
readonly body: unknown;
}

interface WorkOSStub {
readonly baseUrl: string;
readonly requests: () => readonly RecordedRequest[];
readonly setKeys: (keys: ReadonlyArray<JWK>) => void;
}

const generateKeypair = async (kid: string): Promise<Keypair> => {
const { publicKey, privateKey } = await generateKeyPair("RS256");
const jwk = await exportJWK(publicKey);
return { kid, publicJwk: { ...jwk, kid, alg: "RS256" }, privateKey };
};

const signAccessToken = (
keypair: Keypair,
claims: {
readonly subject?: string;
readonly organizationId?: string;
readonly sessionId?: string;
readonly expiresIn?: string | number;
} = {},
) => {
const jwt = new SignJWT({
org_id: claims.organizationId ?? "org_test",
sid: claims.sessionId ?? "session_test",
})
.setProtectedHeader({ alg: "RS256", kid: keypair.kid })
.setSubject(claims.subject ?? USER.id)
.setIssuedAt();

return (
typeof claims.expiresIn === "number"
? jwt.setExpirationTime(claims.expiresIn)
: jwt.setExpirationTime(claims.expiresIn ?? "5m")
).sign(keypair.privateKey);
};

const sealSession = async (accessToken: string, refreshToken = "refresh_test"): Promise<string> =>
`${await sealIron(
{
accessToken,
refreshToken,
user: USER,
},
{ id: "1", secret: COOKIE_PASSWORD },
{ ...ironDefaults, ttl: 0, encode: JSON.stringify },
)}~2`;

const workosUserResponse = {
object: "user",
id: USER.id,
email: USER.email,
email_verified: true,
profile_picture_url: USER.profilePictureUrl,
first_name: USER.firstName,
last_name: USER.lastName,
last_sign_in_at: null,
locale: null,
created_at: "2026-07-09T00:00:00.000Z",
updated_at: "2026-07-09T00:00:00.000Z",
external_id: null,
metadata: {},
} as const;

const decodeJsonBody = Schema.decodeUnknownSync(Schema.fromJsonString(Schema.Unknown));

const readJsonBody = async (request: IncomingMessage): Promise<unknown> => {
const chunks: Buffer[] = [];
for await (const chunk of request) {
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
}
const body = Buffer.concat(chunks).toString("utf8");
return body ? decodeJsonBody(body) : null;
};

const writeJson = (response: ServerResponse, status: number, body: unknown): void => {
response.writeHead(status, { "content-type": "application/json" });
response.end(JSON.stringify(body));
};

const withWorkOSStub = async <A>(
keypair: Keypair,
use: (stub: WorkOSStub) => Promise<A>,
): Promise<A> => {
let keys: ReadonlyArray<JWK> = [keypair.publicJwk];
const requests: RecordedRequest[] = [];
const server = createServer((request, response) => {
// oxlint-disable-next-line executor/no-promise-catch -- boundary: Node's request callback cannot await, so the fixture converts async failures into a stable HTTP 500
void (async () => {
const path = request.url ?? "/";
const body = request.method === "POST" ? await readJsonBody(request) : null;
requests.push({ method: request.method ?? "GET", path, body });

if (request.method === "GET" && path === `/sso/jwks/${CLIENT_ID}`) {
writeJson(response, 200, { keys: keys.map((key) => ({ ...key })) } satisfies JSONWebKeySet);
return;
}

if (request.method === "POST" && path === "/user_management/authenticate") {
const refreshed = await signAccessToken(keypair, {
sessionId: "session_refreshed",
});
writeJson(response, 200, {
user: workosUserResponse,
organization_id: "org_test",
access_token: refreshed,
refresh_token: "refresh_next",
authentication_method: "Password",
});
return;
}

writeJson(response, 404, { error: "unexpected request" });
})().catch((_error: unknown) => {
writeJson(response, 500, {
error: "fixture server failed",
});
});
});

await new Promise<void>((resolve) => server.listen(0, "127.0.0.1", resolve));
const address = server.address() as AddressInfo;
const stub: WorkOSStub = {
baseUrl: `http://127.0.0.1:${address.port}`,
requests: () => requests,
setKeys: (next) => {
keys = next;
},
};

return use(stub).finally(() => {
server.closeAllConnections();
server.close();
});
};

import { collectRawWorkOSList, collectWorkOSList } from "./workos";
const runAuthenticate = (sessionData: string, baseUrl: string) => {
Object.assign(env, {
WORKOS_API_KEY: API_KEY,
WORKOS_CLIENT_ID: CLIENT_ID,
WORKOS_COOKIE_PASSWORD: COOKIE_PASSWORD,
WORKOS_API_URL: baseUrl,
});

return Effect.runPromise(
Effect.gen(function* () {
const workos = yield* WorkOSClient;
return yield* workos.authenticateSealedSession(sessionData);
}).pipe(Effect.provide(WorkOSClient.Default)),
);
};

describe("authenticateSealedSession", () => {
it("validates a sealed session locally with the cached JWKS", async () => {
const keypair = await generateKeypair("k_valid");
await withWorkOSStub(keypair, async (stub) => {
const token = await signAccessToken(keypair, {
organizationId: "org_test",
sessionId: "session_valid",
});
const session = await sealSession(token);

const result = await runAuthenticate(session, stub.baseUrl);

expect(result).toEqual({
userId: USER.id,
email: USER.email,
firstName: USER.firstName,
lastName: USER.lastName,
avatarUrl: USER.profilePictureUrl,
organizationId: "org_test",
sessionId: "session_valid",
refreshedSession: undefined,
});
expect(stub.requests()).toEqual([
{ method: "GET", path: `/sso/jwks/${CLIENT_ID}`, body: null },
]);
});
});

it("reuses the module cached JWKS for a second sealed session", async () => {
const keypair = await generateKeypair("k_cached");
await withWorkOSStub(keypair, async (stub) => {
const first = await sealSession(
await signAccessToken(keypair, {
sessionId: "session_first",
}),
);
const second = await sealSession(
await signAccessToken(keypair, {
sessionId: "session_second",
}),
);

await runAuthenticate(first, stub.baseUrl);
const afterFirst = stub.requests().length;
const result = await runAuthenticate(second, stub.baseUrl);

expect(afterFirst).toBe(1);
expect(result?.sessionId).toBe("session_second");
expect(stub.requests()).toEqual([
{ method: "GET", path: `/sso/jwks/${CLIENT_ID}`, body: null },
]);
});
});

it("falls through to SDK refresh when the access token is expired", async () => {
const keypair = await generateKeypair("k_expired");
await withWorkOSStub(keypair, async (stub) => {
const expiredAt = Math.floor(Date.now() / 1000) - 60;
const expired = await signAccessToken(keypair, {
expiresIn: expiredAt,
sessionId: "session_expired",
});
const session = await sealSession(expired, "refresh_expired");

const result = await runAuthenticate(session, stub.baseUrl);

expect(result?.sessionId).toBe("session_refreshed");
expect(result?.refreshedSession).toEqual(expect.any(String));
expect(stub.requests().map((request) => [request.method, request.path])).toEqual([
["GET", `/sso/jwks/${CLIENT_ID}`],
["POST", "/user_management/authenticate"],
]);
expect(stub.requests()[1]?.body).toMatchObject({
grant_type: "refresh_token",
client_id: CLIENT_ID,
client_secret: API_KEY,
refresh_token: "refresh_expired",
organization_id: "org_test",
});
});
});

it("returns null for garbage session data", async () => {
const keypair = await generateKeypair("k_garbage");
await withWorkOSStub(keypair, async (stub) => {
const result = await runAuthenticate("not-a-sealed-session", stub.baseUrl);

expect(result).toBeNull();
expect(stub.requests()).toEqual([]);
});
});
});

describe("collectWorkOSList", () => {
it("collects memberships beyond the first WorkOS page", async () => {
Expand Down
Loading
Loading