diff --git a/apps/server/src/cloud/publicConfig.test.ts b/apps/server/src/cloud/publicConfig.test.ts index 4cce901fa55a58e0bc4530dd2360624af6611f91..c46e2671a46e8a61655413445ed139c8a6c770c7 100644 --- a/apps/server/src/cloud/publicConfig.test.ts +++ b/apps/server/src/cloud/publicConfig.test.ts @@ -1,6 +1,7 @@ import { assert, it } from "@effect/vitest"; import * as ConfigProvider from "effect/ConfigProvider"; import * as Effect from "effect/Effect"; +import * as Result from "effect/Result"; import { makeCloudCliOAuthConfig, @@ -88,6 +89,27 @@ it.effect("requires Clerk OAuth config when the server bundle has no injected va }).pipe(provideEnv({}), Effect.flip), ); +it.effect("reports malformed Clerk publishable keys as typed configuration failures", () => + Effect.gen(function* () { + const result = yield* makeCloudCliOAuthConfig({ + clerkPublishableKeyFallback: "pk_test_not-base64!!", + clerkCliOAuthClientIdFallback: "oauth_client_embedded", + }).pipe(provideEnv({}), Effect.result); + + assert.isTrue(Result.isFailure(result)); + if (Result.isFailure(result)) { + assert.equal(result.failure.cause._tag, "SourceError"); + if (result.failure.cause._tag === "SourceError") { + assert.equal( + result.failure.cause.message, + "Failed to derive Clerk Frontend API URL from the publishable key.", + ); + assert.instanceOf(result.failure.cause.cause, Error); + } + } + }), +); + it("resolves relay client tracing from runtime config with build-time fallback", () => { const fallback = { tracesUrl: "https://embedded.example.test/v1/traces", diff --git a/apps/server/src/cloud/publicConfig.ts b/apps/server/src/cloud/publicConfig.ts index b344107d75648b695fdf5b6b4a524cb4b7db4182..176b31d7566ac6a2ece10b2ed43ec5a51c22ef92 100644 --- a/apps/server/src/cloud/publicConfig.ts +++ b/apps/server/src/cloud/publicConfig.ts @@ -1,6 +1,7 @@ import { clerkFrontendApiUrlFromPublishableKey } from "@t3tools/shared/relayAuth"; import { normalizeSecureRelayUrl } from "@t3tools/shared/relayUrl"; import * as Config from "effect/Config"; +import * as ConfigProvider from "effect/ConfigProvider"; import * as Effect from "effect/Effect"; import * as Option from "effect/Option"; import * as Schema from "effect/Schema"; @@ -131,16 +132,29 @@ export function makeCloudCliOAuthConfig({ clerkCliOAuthClientIdFallback, ), }).pipe( - Config.map(({ clerkPublishableKey, clientId }) => { - const clerkFrontendApiUrl = clerkFrontendApiUrlFromPublishableKey(clerkPublishableKey); - return { - authorizationEndpoint: `${clerkFrontendApiUrl}/oauth/authorize`, - tokenEndpoint: `${clerkFrontendApiUrl}/oauth/token`, - clientId, - redirectUri: CLOUD_CLI_OAUTH_REDIRECT_URI, - scopes: CLOUD_CLI_OAUTH_SCOPES, - } satisfies CloudCliOAuthConfig; - }), + Config.mapOrFail(({ clerkPublishableKey, clientId }) => + Effect.try({ + try: () => clerkFrontendApiUrlFromPublishableKey(clerkPublishableKey), + catch: (cause) => + new Config.ConfigError( + new ConfigProvider.SourceError({ + message: "Failed to derive Clerk Frontend API URL from the publishable key.", + cause, + }), + ), + }).pipe( + Effect.map( + (clerkFrontendApiUrl) => + ({ + authorizationEndpoint: `${clerkFrontendApiUrl}/oauth/authorize`, + tokenEndpoint: `${clerkFrontendApiUrl}/oauth/token`, + clientId, + redirectUri: CLOUD_CLI_OAUTH_REDIRECT_URI, + scopes: CLOUD_CLI_OAUTH_SCOPES, + }) satisfies CloudCliOAuthConfig, + ), + ), + ), ); }