import { describe, expect, it } from "@effect/vitest"; import { Effect } from "effect"; import { SignJWT, createLocalJWKSet, exportJWK, generateKeyPair } from "jose"; import { McpJwtVerificationError, verifyMcpAccessToken, verifyWorkOSMcpAccessToken, } from "./mcp-auth"; const issuer = "https://test-authkit.example.com"; const resource = "https://test-resource.example.com/mcp"; const otherResource = "https://other-resource.example.com/mcp"; const workosApplicationClientId = "client_workos_application_fixture"; const dynamicOAuthClientId = "client_dynamic_oauth_fixture"; const makeVerifier = async () => { const { publicKey, privateKey } = await generateKeyPair("RS256"); const jwk = await exportJWK(publicKey); const jwks = createLocalJWKSet({ keys: [{ ...jwk, kid: "test-key" }] }); const sign = (claims: Record) => new SignJWT({ org_id: "org_test", ...claims }) .setProtectedHeader({ alg: "RS256", kid: "test-key" }) .setIssuer(issuer) .setSubject("user_test") .setIssuedAt() .setExpirationTime("5m") .sign(privateKey); return { jwks, sign }; }; describe("MCP AuthKit token verification", () => { it.effect("low-level verifier rejects mismatched audience when audience is required", () => Effect.gen(function* () { const { jwks, sign } = yield* Effect.promise(() => makeVerifier()); const token = yield* Effect.promise(() => sign({ aud: otherResource })); const error = yield* Effect.flip(verifyMcpAccessToken(token, jwks, { issuer, audience: resource, })); expect(error).toBeInstanceOf(McpJwtVerificationError); }), ); it.effect("low-level verifier accepts a matching resource audience", () => Effect.gen(function* () { const { jwks, sign } = yield* Effect.promise(() => makeVerifier()); const token = yield* Effect.promise(() => sign({ aud: resource })); const verified = yield* verifyMcpAccessToken(token, jwks, { issuer, audience: resource, }); expect(verified).toEqual({ accountId: "user_test", organizationId: "org_test", }); }), ); it.effect("MCP verifier accepts WorkOS application-audience tokens", () => Effect.gen(function* () { const { jwks, sign } = yield* Effect.promise(() => makeVerifier()); const token = yield* Effect.promise(() => sign({ aud: workosApplicationClientId, sid: "app_consent_test" }), ); const verified = yield* verifyWorkOSMcpAccessToken(token, jwks, { issuer, audience: workosApplicationClientId, }); expect(verified).toEqual({ accountId: "user_test", organizationId: "org_test", }); }), ); it.effect("MCP verifier rejects resource-audience tokens", () => Effect.gen(function* () { const { jwks, sign } = yield* Effect.promise(() => makeVerifier()); const token = yield* Effect.promise(() => sign({ aud: resource })); const error = yield* Effect.flip( verifyWorkOSMcpAccessToken(token, jwks, { issuer, audience: workosApplicationClientId, }), ); expect(error).toBeInstanceOf(McpJwtVerificationError); }), ); it.effect("MCP verifier rejects dynamic OAuth client-audience tokens", () => Effect.gen(function* () { const { jwks, sign } = yield* Effect.promise(() => makeVerifier()); const token = yield* Effect.promise(() => sign({ aud: dynamicOAuthClientId, sid: "app_consent_test" }), ); const error = yield* Effect.flip( verifyWorkOSMcpAccessToken(token, jwks, { issuer, audience: workosApplicationClientId, }), ); expect(error).toBeInstanceOf(McpJwtVerificationError); }), ); });