import * as NodeCrypto from "node:crypto"; import { describe, expect, it } from "@effect/vitest"; import { signRelayJwt } from "@t3tools/shared/relayJwt"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Redacted from "effect/Redacted"; import * as RelayConfiguration from "../Config.ts"; import * as RelayTokens from "./RelayTokens.ts"; const keyPair = NodeCrypto.generateKeyPairSync("ed25519", { privateKeyEncoding: { format: "pem", type: "pkcs8" }, publicKeyEncoding: { format: "pem", type: "spki" }, }); const config = RelayConfiguration.RelayConfiguration.of({ relayIssuer: "https://relay.example.test/", apns: { environment: "sandbox", teamId: "team-id", keyId: "key-id", privateKey: Redacted.make("private-key"), bundleId: "com.t3tools.t3code.dev", }, apnsDeliveryJobSigningSecret: Redacted.make("job-secret"), clerkSecretKey: Redacted.make("clerk-secret"), clerkPublishableKey: "pk_test_test", clerkJwtAudience: "t3-code-relay", cloudMintPrivateKey: Redacted.make(keyPair.privateKey), cloudMintPublicKey: keyPair.publicKey, managedEndpointBaseDomain: undefined, managedEndpointNamespace: undefined, }); const layer = RelayTokens.layer.pipe(Layer.provide(RelayConfiguration.layer(config))); describe("RelayTokens", () => { it.effect("issues a user-bound environment link challenge", () => Effect.gen(function* () { const relayTokens = yield* RelayTokens.RelayTokens; const token = yield* relayTokens.issueLinkChallenge({ userId: "user_123", request: { notificationsEnabled: true, liveActivitiesEnabled: true, managedTunnelsEnabled: true, }, jti: "challenge-1", issuedAtEpochSeconds: 100, expiresAtEpochSeconds: 200, }); expect( yield* relayTokens.verifyLinkChallenge({ token, userId: "user_123", request: { notificationsEnabled: true, liveActivitiesEnabled: true, managedTunnelsEnabled: true, }, nowEpochSeconds: 150, }), ).toMatchObject({ sub: "user_123", jti: "challenge-1" }); expect( yield* relayTokens.verifyLinkChallenge({ token, userId: "attacker", request: { notificationsEnabled: true, liveActivitiesEnabled: true, managedTunnelsEnabled: true, }, nowEpochSeconds: 150, }), ).toBeNull(); }).pipe(Effect.provide(layer)), ); it.effect("issues and verifies DPoP access tokens bound to one proof-key thumbprint", () => Effect.gen(function* () { const relayTokens = yield* RelayTokens.RelayTokens; const token = yield* relayTokens.issueDpopAccessToken({ userId: "user_123", proofKeyThumbprint: "proof-key-thumbprint", jti: "access-token-1", issuedAtEpochSeconds: 100, expiresAtEpochSeconds: 1_900, clientId: "t3-mobile", scopes: ["environment:connect", "environment:status", "mobile:registration"], }); expect( yield* relayTokens.verifyDpopAccessToken({ token, nowEpochSeconds: 700 }), ).toMatchObject({ sub: "user_123", cnf: { jkt: "proof-key-thumbprint" }, client_id: "t3-mobile", scope: ["environment:connect", "environment:status", "mobile:registration"], }); expect( yield* relayTokens.verifyDpopAccessToken({ token, nowEpochSeconds: 1_961 }), ).toBeNull(); }).pipe(Effect.provide(layer)), ); it.effect("issues tunnel-only DPoP access tokens to web public clients", () => Effect.gen(function* () { const relayTokens = yield* RelayTokens.RelayTokens; const token = yield* relayTokens.issueDpopAccessToken({ userId: "user_123", proofKeyThumbprint: "web-proof-key-thumbprint", jti: "web-access-token-1", issuedAtEpochSeconds: 100, expiresAtEpochSeconds: 200, clientId: "t3-web", scopes: ["environment:connect", "environment:status"], }); expect( yield* relayTokens.verifyDpopAccessToken({ token, nowEpochSeconds: 150 }), ).toMatchObject({ client_id: "t3-web", scope: ["environment:connect", "environment:status"], cnf: { jkt: "web-proof-key-thumbprint" }, }); }).pipe(Effect.provide(layer)), ); it.effect("treats requested scope as an order-independent set", () => Effect.gen(function* () { const relayTokens = yield* RelayTokens.RelayTokens; expect( relayTokens.resolveDpopAccessTokenScopes({ clientId: "t3-mobile", scope: "environment:status environment:connect environment:status", }), ).toEqual(["environment:status", "environment:connect"]); }).pipe(Effect.provide(layer)), ); it.effect("rejects signed DPoP tokens whose scope is outside the relay policy", () => Effect.gen(function* () { const relayTokens = yield* RelayTokens.RelayTokens; const token = yield* signRelayJwt({ privateKey: keyPair.privateKey, typ: "t3-relay-dpop-access+jwt", payload: { iss: "https://relay.example.test", aud: "https://relay.example.test", sub: "user_123", jti: "access-token-invalid-scope", iat: 100, exp: 200, client_id: "t3-mobile", scope: "environment:admin", cnf: { jkt: "proof-key-thumbprint" }, }, }); expect(yield* relayTokens.verifyDpopAccessToken({ token, nowEpochSeconds: 150 })).toBeNull(); }).pipe(Effect.provide(layer)), ); it.effect("rejects mobile registration scope on a web public client token", () => Effect.gen(function* () { const relayTokens = yield* RelayTokens.RelayTokens; const token = yield* signRelayJwt({ privateKey: keyPair.privateKey, typ: "t3-relay-dpop-access+jwt", payload: { iss: "https://relay.example.test", aud: "https://relay.example.test", sub: "user_123", jti: "web-token-invalid-mobile-scope", iat: 100, exp: 200, client_id: "t3-web", scope: "environment:connect mobile:registration", cnf: { jkt: "proof-key-thumbprint" }, }, }); expect(yield* relayTokens.verifyDpopAccessToken({ token, nowEpochSeconds: 150 })).toBeNull(); }).pipe(Effect.provide(layer)), ); });