import { createClerkClient, verifyToken } from "@clerk/backend";import { sql as drizzleSql } from "drizzle-orm";import * as Crypto from "effect/Crypto";import * as Context from "effect/Context";import * as DateTime from "effect/DateTime";import * as Duration from "effect/Duration";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as Record from "effect/Record";import * as Redacted from "effect/Redacted";import * as Schema from "effect/Schema";import * as Tracer from "effect/Tracer";import * as HttpEffect from "effect/unstable/http/HttpEffect";import * as HttpMiddleware from "effect/unstable/http/HttpMiddleware";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 * as HttpTraceContext from "effect/unstable/http/HttpTraceContext";import * as HttpApiBuilder from "effect/unstable/httpapi/HttpApiBuilder";import * as HttpApiError from "effect/unstable/httpapi/HttpApiError";import { encodeOAuthScope } from "@t3tools/shared/oauthScope";import { httpHeaderRedactionLayer } from "@t3tools/shared/httpObservability"; import { RelayApi, RelayAgentActivityPublishProofExpiredError, RelayAgentActivityPublishProofInvalidError, RelayClientAuth, RelayClientPrincipal, RelayAccessTokenType, RelayDpopClientAuth, RelayEnvironmentConnectScope, RelayEnvironmentStatusScope, RelayMobileRegistrationScope, RelayAuthInvalidError, type RelayAuthInvalidReason, RelayEnvironmentAuth, RelayEnvironmentConnectNotAuthorizedError, RelayEnvironmentEndpointTimedOutError, RelayEnvironmentEndpointUnavailableError, RelayEnvironmentLinkFailedError, RelayEnvironmentLinkProofExpiredError, RelayEnvironmentLinkProofInvalidError, RelayEnvironmentLinkUnavailableError, RelayEnvironmentPrincipal, type RelayEnvironmentConnectRequest, type RelayDpopAccessTokenScope, RelayInternalError,} from "@t3tools/contracts/relay";import { normalizeRelayIssuer } from "@t3tools/shared/relayJwt"; import * as DeliveryAttempts from "../agentActivity/DeliveryAttempts.ts";import * as AgentActivityRows from "../agentActivity/AgentActivityRows.ts";import * as Devices from "../agentActivity/Devices.ts";import * as DpopProofs from "../auth/DpopProofs.ts";import * as RelayTokens from "../auth/RelayTokens.ts";import * as EnvironmentCredentials from "../environments/EnvironmentCredentials.ts";import * as EnvironmentLinks from "../environments/EnvironmentLinks.ts";import * as LiveActivities from "../agentActivity/LiveActivities.ts";import * as RelayConfiguration from "../Config.ts";import * as AgentActivityPublisher from "../agentActivity/AgentActivityPublisher.ts";import * as EnvironmentConnector from "../environments/EnvironmentConnector.ts";import * as EnvironmentLinker from "../environments/EnvironmentLinker.ts";import * as ManagedEndpointProvider from "../environments/ManagedEndpointProvider.ts";import * as ManagedEndpointAllocations from "../environments/ManagedEndpointAllocations.ts";import * as EnvironmentPublishSignatures from "../environments/EnvironmentPublishSignatures.ts";import * as MobileRegistrations from "../agentActivity/MobileRegistrations.ts";import { withSpanAttributes } from "../observability.ts";import * as RelayDb from "../db.ts"; const relayCorsAllowedMethods = ["GET", "POST", "DELETE", "OPTIONS"] as const;const relayCorsAllowedHeaders = [ "authorization", "b3", "traceparent", "content-type", "dpop",] as const;const relayCorsExposedHeaders = ["traceparent", "www-authenticate"] as const; const relayCorsHeaders = { "access-control-allow-origin": "*", "access-control-expose-headers": relayCorsExposedHeaders.join(","),} as const; const relayCorsPreflightHeaders = { ...relayCorsHeaders, "access-control-allow-methods": relayCorsAllowedMethods.join(","), "access-control-allow-headers": relayCorsAllowedHeaders.join(","), "access-control-max-age": "86400",} as const; const appendRelayCredentialResponseHeaders = HttpEffect.appendPreResponseHandler( (_request, response) => Effect.succeed( HttpServerResponse.setHeaders(response, { "cache-control": "no-store", pragma: "no-cache", }), ),); const appendRelayDpopChallengeHeader = HttpEffect.appendPreResponseHandler((_request, response) => Effect.succeed( response.status === 401 ? HttpServerResponse.setHeader(response, "www-authenticate", "DPoP") : response, ),); const appendRelayTraceContextResponseHeader = Effect.gen(function* () { const span = yield* Effect.currentParentSpan; if (span._tag !== "Span") { return; } const traceparent = HttpTraceContext.toHeaders(span).traceparent; if (traceparent === undefined) { return; } yield* HttpEffect.appendPreResponseHandler((_request, response) => Effect.succeed(HttpServerResponse.setHeader(response, "traceparent", traceparent)), );}).pipe(Effect.ignore); export const relayCors = HttpRouter.middleware( Effect.fnUntraced(function* <E, R>( httpEffect: Effect.Effect< HttpServerResponse.HttpServerResponse, E, HttpServerRequest.HttpServerRequest | R >, ) { const request = yield* HttpServerRequest.HttpServerRequest; if (request.method === "OPTIONS") { return HttpServerResponse.empty({ status: 204, headers: relayCorsPreflightHeaders, }); } const response = yield* httpEffect; return HttpServerResponse.setHeaders(response, relayCorsHeaders); }), { global: true },); export const relayNotFoundRoute = HttpRouter.add( "*", "/*", HttpServerResponse.empty({ status: 404 }),); export const relayDocsRedirectRoute = HttpRouter.add( "GET", "/", HttpServerResponse.redirect("/docs"),); export const traceRelayHttpRequest = <E, R>( httpEffect: Effect.Effect< HttpServerResponse.HttpServerResponse, E, HttpServerRequest.HttpServerRequest | R >,) => // HttpMiddleware finalizes its span on the dispatcher; do not close a request-scoped exporter first. HttpMiddleware.tracer( appendRelayTraceContextResponseHeader.pipe(Effect.andThen(httpEffect)), ).pipe(Effect.ensuring(Effect.yieldNow)); export const traceRelayHttpRequestWith = <E, R, LayerError, LayerRequirements>( httpEffect: Effect.Effect< HttpServerResponse.HttpServerResponse, E, HttpServerRequest.HttpServerRequest | R >, tracerLayer: Layer.Layer<never, LayerError, LayerRequirements>,) => traceRelayHttpRequest(httpEffect).pipe( Effect.provide(Layer.merge(tracerLayer, httpHeaderRedactionLayer)), ); export const withoutCapturedParentSpan = <A, E, R>( effect: Effect.Effect<A, E, R>,): Effect.Effect<A, E, R> => Effect.withFiber((fiber) => { const context = fiber.context; // HttpApiBuilder captures its build context for route handlers; an event parent would outlive export. fiber.setContext(Context.omit(Tracer.ParentSpan)(context)); return effect.pipe(Effect.ensuring(Effect.sync(() => fiber.setContext(context)))); }); export const relayClientAuthLayer = Layer.effect( RelayClientAuth, Effect.gen(function* () { const config = yield* RelayConfiguration.RelayConfiguration; return { clientBearer: Effect.fn("relay.auth.client.bearer")(function* (httpEffect, { credential }) { const token = readHttpAuthorizationCredential(credential); const verified = yield* verifyRelayClientBearerToken(config, token).pipe( Effect.tapError((error) => Effect.annotateCurrentSpan( "relay.auth.clerk_verification_failure", clerkVerificationFailureReason(error.cause), ), ), Effect.catch(() => relayAuthInvalidError("invalid_bearer")), ); if (!verified.sub) { yield* Effect.annotateCurrentSpan({ "relay.auth.clerk_verification_failure": "missing_subject", }); return yield* relayAuthInvalidError("invalid_bearer"); } yield* Effect.annotateCurrentSpan({ "relay.auth.mode": verified.mode, "relay.auth.subject": verified.sub, }); return yield* httpEffect.pipe( withSpanAttributes({ "user.id": verified.sub }), Effect.provideService(RelayClientPrincipal, { userId: verified.sub, token, }), ); }), }; }),); export const relayEnvironmentAuthLayer = Layer.effect( RelayEnvironmentAuth, Effect.gen(function* () { const credentials = yield* EnvironmentCredentials.EnvironmentCredentials; return { environmentBearer: Effect.fn("relay.auth.environment.bearer")(function* ( httpEffect, { credential }, ) { const token = readHttpAuthorizationCredential(credential); const principal = yield* credentials.authenticate(token).pipe( Effect.catchTags({ EnvironmentCredentialAuthenticatePersistenceError: () => relayInternalErrorResponse("persistence_failed"), }), ); if (principal._tag === "None") { return yield* relayAuthInvalidError("not_authorized"); } yield* Effect.annotateCurrentSpan({ "relay.auth.mode": "environment_credential", }); return yield* httpEffect.pipe( withSpanAttributes({ "relay.environment_id": principal.value.environmentId, }), Effect.provideService(RelayEnvironmentPrincipal, principal.value), ); }), }; }),); export const relayDpopClientAuthLayer = Layer.effect( RelayDpopClientAuth, Effect.gen(function* () { const relayTokens = yield* RelayTokens.RelayTokens; return { relayDpop: Effect.fn("relay.auth.dpop_client")(function* (httpEffect, { credential }) { yield* appendRelayDpopChallengeHeader; const request = yield* HttpServerRequest.HttpServerRequest; if (!isDpopAuthorizationHeader(request.headers.authorization)) { return yield* relayAuthInvalidError("invalid_bearer"); } const token = readHttpAuthorizationCredential(credential); const now = yield* DateTime.now; const verified = yield* relayTokens.verifyDpopAccessToken({ token, nowEpochSeconds: Math.floor(now.epochMilliseconds / 1_000), }); if (!verified) { return yield* relayAuthInvalidError("invalid_bearer"); } yield* Effect.annotateCurrentSpan({ "relay.auth.mode": "dpop", "relay.auth.subject": verified.sub, }); return yield* httpEffect.pipe( withSpanAttributes({ "user.id": verified.sub }), Effect.provideService(RelayClientPrincipal, { userId: verified.sub, token, proofKeyThumbprint: verified.cnf.jkt, dpopScopes: verified.scope, }), ); }), }; }),); function isDpopAuthorizationHeader(value: string | undefined): boolean { return /^DPoP +/iu.test(value ?? "");} function readHttpAuthorizationCredential(credential: Redacted.Redacted<string>): string { // Effect beta.73 leaves the scheme separator in decoded HTTP credentials. return Redacted.value(credential).trimStart();} export const metadataApi = HttpApiBuilder.group( RelayApi, "metadata", Effect.fnUntraced(function* (handlers) { const settings = yield* RelayConfiguration.RelayConfiguration; const issuer = normalizeRelayIssuer(settings.relayIssuer); const scopes = [ RelayEnvironmentConnectScope, RelayEnvironmentStatusScope, RelayMobileRegistrationScope, ] as const; return handlers .handle("authorizationServer", () => Effect.succeed({ issuer, token_endpoint: `${issuer}/v1/client/dpop-token`, grant_types_supported: ["urn:ietf:params:oauth:grant-type:token-exchange"], token_endpoint_auth_methods_supported: ["none"], dpop_signing_alg_values_supported: ["ES256"], scopes_supported: scopes, }), ) .handle("protectedResource", () => Effect.succeed({ resource: issuer, authorization_servers: [issuer], scopes_supported: scopes, dpop_bound_access_tokens_required: true, dpop_signing_alg_values_supported: ["ES256"], }), ); }),); export const healthApi = HttpApiBuilder.group( RelayApi, "health", Effect.fnUntraced(function* (handlers) { const db = yield* RelayDb.RelayDb; return handlers.handle( "health", Effect.fn("relay.api.health")( function* () { yield* db.execute(drizzleSql`SELECT 1`); return { ok: true, service: "relay" as const }; }, Effect.catch(() => relayInternalErrorResponse("database_unavailable")), ), ); }),); export const mobileApi = HttpApiBuilder.group( RelayApi, "mobile", Effect.fnUntraced(function* (handlers) { const registrations = yield* MobileRegistrations.MobileRegistrations; const dpopProofs = yield* DpopProofs.DpopProofReplay; return handlers .handle( "registerDevice", Effect.fn("relay.api.mobile.registerDevice")(function* (args) { const { payload } = args; const { userId, token } = yield* RelayClientPrincipal; const proofKeyThumbprint = yield* requireDpopPrincipalScope("mobile:registration"); yield* requireDpopThumbprint(proofKeyThumbprint, { expectedAccessToken: token, }).pipe(Effect.provideService(DpopProofs.DpopProofReplay, dpopProofs)); return yield* registrations.registerDevice({ userId, payload }); }, mapRelayCommonApiErrors("invalid_dpop")), ) .handle( "registerLiveActivity", Effect.fn("relay.api.mobile.registerLiveActivity")(function* (args) { const { payload } = args; const { userId, token } = yield* RelayClientPrincipal; const proofKeyThumbprint = yield* requireDpopPrincipalScope("mobile:registration"); yield* requireDpopThumbprint(proofKeyThumbprint, { expectedAccessToken: token, }).pipe(Effect.provideService(DpopProofs.DpopProofReplay, dpopProofs)); return yield* registrations.registerLiveActivity({ userId, payload }); }, mapRelayCommonApiErrors("invalid_dpop")), ) .handle( "unregisterDevice", Effect.fn("relay.api.mobile.unregisterDevice")(function* (args) { const { params } = args; const { userId, token } = yield* RelayClientPrincipal; const proofKeyThumbprint = yield* requireDpopPrincipalScope("mobile:registration"); yield* requireDpopThumbprint(proofKeyThumbprint, { expectedAccessToken: token, }).pipe(Effect.provideService(DpopProofs.DpopProofReplay, dpopProofs)); return yield* registrations.unregisterDevice({ userId, deviceId: params.deviceId }); }, mapRelayCommonApiErrors("invalid_dpop")), ); }),); export const clientApi = HttpApiBuilder.group( RelayApi, "client", Effect.fnUntraced(function* (handlers) { const config = yield* RelayConfiguration.RelayConfiguration; const crypto = yield* Crypto.Crypto; const relayTokens = yield* RelayTokens.RelayTokens; const linker = yield* EnvironmentLinker.EnvironmentLinker; const links = yield* EnvironmentLinks.EnvironmentLinks; const managedEndpointProvider = yield* ManagedEndpointProvider.ManagedEndpointProvider; const credentials = yield* EnvironmentCredentials.EnvironmentCredentials; const devices = yield* Devices.Devices; return handlers .handle( "listEnvironments", Effect.fn("relay.api.client.listEnvironments")(function* () { const { userId } = yield* RelayClientPrincipal; const environments = yield* links.listForUser({ userId }); return { environments }; }, mapRelayCommonApiErrors("not_authorized")), ) .handle( "listDevices", Effect.fn("relay.api.client.listDevices")(function* () { const { userId } = yield* RelayClientPrincipal; return { devices: yield* devices.listForUser({ userId }) }; }, mapRelayCommonApiErrors("not_authorized")), ) .handle( "linkEnvironment", Effect.fn("relay.api.client.linkEnvironment")( function* (args) { const { payload } = args; yield* appendRelayCredentialResponseHeaders; const { userId } = yield* RelayClientPrincipal; const result = yield* linker.link({ userId, request: payload }); return { ok: true, cloudUserId: userId, environmentId: result.environmentId, endpoint: result.endpoint, endpointRuntime: result.endpointRuntime, relayIssuer: config.relayIssuer, environmentCredential: result.environmentCredential, cloudMintPublicKey: config.cloudMintPublicKey, }; }, mapErrorTags({ EnvironmentLinkProofExpired: (_error, traceId) => new RelayEnvironmentLinkProofExpiredError({ code: "environment_link_proof_expired", traceId, }), EnvironmentLinkProofInvalid: (linkError, traceId) => new RelayEnvironmentLinkProofInvalidError({ code: "environment_link_proof_invalid", reason: linkError.reason, traceId, }), ManagedEndpointProvisioningNotConfigured: (_error, traceId) => new RelayEnvironmentLinkUnavailableError({ code: "environment_link_unavailable", reason: "managed_endpoint_not_configured", traceId, }), ManagedEndpointProvisioningFailed: (_error, traceId) => new RelayEnvironmentLinkUnavailableError({ code: "environment_link_unavailable", reason: "managed_endpoint_provisioning_failed", traceId, }), ManagedEndpointOriginNotAllowed: (_error, traceId) => new RelayEnvironmentLinkProofInvalidError({ code: "environment_link_proof_invalid", reason: "origin_not_allowed", traceId, }), EnvironmentLinkUpsertPersistenceError: (_error, traceId) => new RelayEnvironmentLinkFailedError({ code: "environment_link_failed", reason: "link_persistence_failed", traceId, }), EnvironmentCredentialCreatePersistenceError: (_error, traceId) => new RelayEnvironmentLinkFailedError({ code: "environment_link_failed", reason: "credential_persistence_failed", traceId, }), DpopProofReplayPersistenceError: (_error, traceId) => new RelayEnvironmentLinkFailedError({ code: "environment_link_failed",Lines 1–500 of 1164. Load more
import { createClerkClient, verifyToken } from "@clerk/backend"; import { sql as drizzleSql } from "drizzle-orm"; import * as Crypto from "effect/Crypto"; import * as Context from "effect/Context"; import * as DateTime from "effect/DateTime"; import * as Duration from "effect/Duration"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Record from "effect/Record"; import * as Redacted from "effect/Redacted"; import * as Schema from "effect/Schema"; import * as Tracer from "effect/Tracer"; import * as HttpEffect from "effect/unstable/http/HttpEffect"; import * as HttpMiddleware from "effect/unstable/http/HttpMiddleware"; 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 * as HttpTraceContext from "effect/unstable/http/HttpTraceContext"; import * as HttpApiBuilder from "effect/unstable/httpapi/HttpApiBuilder"; import * as HttpApiError from "effect/unstable/httpapi/HttpApiError"; import { encodeOAuthScope } from "@t3tools/shared/oauthScope"; import { httpHeaderRedactionLayer } from "@t3tools/shared/httpObservability"; import { RelayApi, RelayAgentActivityPublishProofExpiredError, RelayAgentActivityPublishProofInvalidError, RelayClientAuth, RelayClientPrincipal, RelayAccessTokenType, RelayDpopClientAuth, RelayEnvironmentConnectScope, RelayEnvironmentStatusScope, RelayMobileRegistrationScope, RelayAuthInvalidError, type RelayAuthInvalidReason, RelayEnvironmentAuth, RelayEnvironmentConnectNotAuthorizedError, RelayEnvironmentEndpointTimedOutError, RelayEnvironmentEndpointUnavailableError, RelayEnvironmentLinkFailedError, RelayEnvironmentLinkProofExpiredError, RelayEnvironmentLinkProofInvalidError, RelayEnvironmentLinkUnavailableError, RelayEnvironmentPrincipal, type RelayEnvironmentConnectRequest, type RelayDpopAccessTokenScope, RelayInternalError, } from "@t3tools/contracts/relay"; import { normalizeRelayIssuer } from "@t3tools/shared/relayJwt"; import * as DeliveryAttempts from "../agentActivity/DeliveryAttempts.ts"; import * as AgentActivityRows from "../agentActivity/AgentActivityRows.ts"; import * as Devices from "../agentActivity/Devices.ts"; import * as DpopProofs from "../auth/DpopProofs.ts"; import * as RelayTokens from "../auth/RelayTokens.ts"; import * as EnvironmentCredentials from "../environments/EnvironmentCredentials.ts"; import * as EnvironmentLinks from "../environments/EnvironmentLinks.ts"; import * as LiveActivities from "../agentActivity/LiveActivities.ts"; import * as RelayConfiguration from "../Config.ts"; import * as AgentActivityPublisher from "../agentActivity/AgentActivityPublisher.ts"; import * as EnvironmentConnector from "../environments/EnvironmentConnector.ts"; import * as EnvironmentLinker from "../environments/EnvironmentLinker.ts"; import * as ManagedEndpointProvider from "../environments/ManagedEndpointProvider.ts"; import * as ManagedEndpointAllocations from "../environments/ManagedEndpointAllocations.ts"; import * as EnvironmentPublishSignatures from "../environments/EnvironmentPublishSignatures.ts"; import * as MobileRegistrations from "../agentActivity/MobileRegistrations.ts"; import { withSpanAttributes } from "../observability.ts"; import * as RelayDb from "../db.ts"; const relayCorsAllowedMethods = ["GET", "POST", "DELETE", "OPTIONS"] as const; const relayCorsAllowedHeaders = [ "authorization", "b3", "traceparent", "content-type", "dpop", ] as const; const relayCorsExposedHeaders = ["traceparent", "www-authenticate"] as const; const relayCorsHeaders = { "access-control-allow-origin": "*", "access-control-expose-headers": relayCorsExposedHeaders.join(","), } as const; const relayCorsPreflightHeaders = { ...relayCorsHeaders, "access-control-allow-methods": relayCorsAllowedMethods.join(","), "access-control-allow-headers": relayCorsAllowedHeaders.join(","), "access-control-max-age": "86400", } as const; const appendRelayCredentialResponseHeaders = HttpEffect.appendPreResponseHandler( (_request, response) => Effect.succeed( HttpServerResponse.setHeaders(response, { "cache-control": "no-store", pragma: "no-cache", }), ), ); const appendRelayDpopChallengeHeader = HttpEffect.appendPreResponseHandler((_request, response) => Effect.succeed( response.status === 401 ? HttpServerResponse.setHeader(response, "www-authenticate", "DPoP") : response, ), ); const appendRelayTraceContextResponseHeader = Effect.gen(function* () { const span = yield* Effect.currentParentSpan; if (span._tag !== "Span") { return; } const traceparent = HttpTraceContext.toHeaders(span).traceparent; if (traceparent === undefined) { return; } yield* HttpEffect.appendPreResponseHandler((_request, response) => Effect.succeed(HttpServerResponse.setHeader(response, "traceparent", traceparent)), ); }).pipe(Effect.ignore); export const relayCors = HttpRouter.middleware( Effect.fnUntraced(function* <E, R>( httpEffect: Effect.Effect< HttpServerResponse.HttpServerResponse, E, HttpServerRequest.HttpServerRequest | R >, ) { const request = yield* HttpServerRequest.HttpServerRequest; if (request.method === "OPTIONS") { return HttpServerResponse.empty({ status: 204, headers: relayCorsPreflightHeaders, }); } const response = yield* httpEffect; return HttpServerResponse.setHeaders(response, relayCorsHeaders); }), { global: true }, ); export const relayNotFoundRoute = HttpRouter.add( "*", "/*", HttpServerResponse.empty({ status: 404 }), ); export const relayDocsRedirectRoute = HttpRouter.add( "GET", "/", HttpServerResponse.redirect("/docs"), ); export const traceRelayHttpRequest = <E, R>( httpEffect: Effect.Effect< HttpServerResponse.HttpServerResponse, E, HttpServerRequest.HttpServerRequest | R >, ) => // HttpMiddleware finalizes its span on the dispatcher; do not close a request-scoped exporter first. HttpMiddleware.tracer( appendRelayTraceContextResponseHeader.pipe(Effect.andThen(httpEffect)), ).pipe(Effect.ensuring(Effect.yieldNow)); export const traceRelayHttpRequestWith = <E, R, LayerError, LayerRequirements>( httpEffect: Effect.Effect< HttpServerResponse.HttpServerResponse, E, HttpServerRequest.HttpServerRequest | R >, tracerLayer: Layer.Layer<never, LayerError, LayerRequirements>, ) => traceRelayHttpRequest(httpEffect).pipe( Effect.provide(Layer.merge(tracerLayer, httpHeaderRedactionLayer)), ); export const withoutCapturedParentSpan = <A, E, R>( effect: Effect.Effect<A, E, R>, ): Effect.Effect<A, E, R> => Effect.withFiber((fiber) => { const context = fiber.context; // HttpApiBuilder captures its build context for route handlers; an event parent would outlive export. fiber.setContext(Context.omit(Tracer.ParentSpan)(context)); return effect.pipe(Effect.ensuring(Effect.sync(() => fiber.setContext(context)))); }); export const relayClientAuthLayer = Layer.effect( RelayClientAuth, Effect.gen(function* () { const config = yield* RelayConfiguration.RelayConfiguration; return { clientBearer: Effect.fn("relay.auth.client.bearer")(function* (httpEffect, { credential }) { const token = readHttpAuthorizationCredential(credential); const verified = yield* verifyRelayClientBearerToken(config, token).pipe( Effect.tapError((error) => Effect.annotateCurrentSpan( "relay.auth.clerk_verification_failure", clerkVerificationFailureReason(error.cause), ), ), Effect.catch(() => relayAuthInvalidError("invalid_bearer")), ); if (!verified.sub) { yield* Effect.annotateCurrentSpan({ "relay.auth.clerk_verification_failure": "missing_subject", }); return yield* relayAuthInvalidError("invalid_bearer"); } yield* Effect.annotateCurrentSpan({ "relay.auth.mode": verified.mode, "relay.auth.subject": verified.sub, }); return yield* httpEffect.pipe( withSpanAttributes({ "user.id": verified.sub }), Effect.provideService(RelayClientPrincipal, { userId: verified.sub, token, }), ); }), }; }), ); export const relayEnvironmentAuthLayer = Layer.effect( RelayEnvironmentAuth, Effect.gen(function* () { const credentials = yield* EnvironmentCredentials.EnvironmentCredentials; return { environmentBearer: Effect.fn("relay.auth.environment.bearer")(function* ( httpEffect, { credential }, ) { const token = readHttpAuthorizationCredential(credential); const principal = yield* credentials.authenticate(token).pipe( Effect.catchTags({ EnvironmentCredentialAuthenticatePersistenceError: () => relayInternalErrorResponse("persistence_failed"), }), ); if (principal._tag === "None") { return yield* relayAuthInvalidError("not_authorized"); } yield* Effect.annotateCurrentSpan({ "relay.auth.mode": "environment_credential", }); return yield* httpEffect.pipe( withSpanAttributes({ "relay.environment_id": principal.value.environmentId, }), Effect.provideService(RelayEnvironmentPrincipal, principal.value), ); }), }; }), ); export const relayDpopClientAuthLayer = Layer.effect( RelayDpopClientAuth, Effect.gen(function* () { const relayTokens = yield* RelayTokens.RelayTokens; return { relayDpop: Effect.fn("relay.auth.dpop_client")(function* (httpEffect, { credential }) { yield* appendRelayDpopChallengeHeader; const request = yield* HttpServerRequest.HttpServerRequest; if (!isDpopAuthorizationHeader(request.headers.authorization)) { return yield* relayAuthInvalidError("invalid_bearer"); } const token = readHttpAuthorizationCredential(credential); const now = yield* DateTime.now; const verified = yield* relayTokens.verifyDpopAccessToken({ token, nowEpochSeconds: Math.floor(now.epochMilliseconds / 1_000), }); if (!verified) { return yield* relayAuthInvalidError("invalid_bearer"); } yield* Effect.annotateCurrentSpan({ "relay.auth.mode": "dpop", "relay.auth.subject": verified.sub, }); return yield* httpEffect.pipe( withSpanAttributes({ "user.id": verified.sub }), Effect.provideService(RelayClientPrincipal, { userId: verified.sub, token, proofKeyThumbprint: verified.cnf.jkt, dpopScopes: verified.scope, }), ); }), }; }), ); function isDpopAuthorizationHeader(value: string | undefined): boolean { return /^DPoP +/iu.test(value ?? ""); } function readHttpAuthorizationCredential(credential: Redacted.Redacted<string>): string { // Effect beta.73 leaves the scheme separator in decoded HTTP credentials. return Redacted.value(credential).trimStart(); } export const metadataApi = HttpApiBuilder.group( RelayApi, "metadata", Effect.fnUntraced(function* (handlers) { const settings = yield* RelayConfiguration.RelayConfiguration; const issuer = normalizeRelayIssuer(settings.relayIssuer); const scopes = [ RelayEnvironmentConnectScope, RelayEnvironmentStatusScope, RelayMobileRegistrationScope, ] as const; return handlers .handle("authorizationServer", () => Effect.succeed({ issuer, token_endpoint: `${issuer}/v1/client/dpop-token`, grant_types_supported: ["urn:ietf:params:oauth:grant-type:token-exchange"], token_endpoint_auth_methods_supported: ["none"], dpop_signing_alg_values_supported: ["ES256"], scopes_supported: scopes, }), ) .handle("protectedResource", () => Effect.succeed({ resource: issuer, authorization_servers: [issuer], scopes_supported: scopes, dpop_bound_access_tokens_required: true, dpop_signing_alg_values_supported: ["ES256"], }), ); }), ); export const healthApi = HttpApiBuilder.group( RelayApi, "health", Effect.fnUntraced(function* (handlers) { const db = yield* RelayDb.RelayDb; return handlers.handle( "health", Effect.fn("relay.api.health")( function* () { yield* db.execute(drizzleSql`SELECT 1`); return { ok: true, service: "relay" as const }; }, Effect.catch(() => relayInternalErrorResponse("database_unavailable")), ), ); }), ); export const mobileApi = HttpApiBuilder.group( RelayApi, "mobile", Effect.fnUntraced(function* (handlers) { const registrations = yield* MobileRegistrations.MobileRegistrations; const dpopProofs = yield* DpopProofs.DpopProofReplay; return handlers .handle( "registerDevice", Effect.fn("relay.api.mobile.registerDevice")(function* (args) { const { payload } = args; const { userId, token } = yield* RelayClientPrincipal; const proofKeyThumbprint = yield* requireDpopPrincipalScope("mobile:registration"); yield* requireDpopThumbprint(proofKeyThumbprint, { expectedAccessToken: token, }).pipe(Effect.provideService(DpopProofs.DpopProofReplay, dpopProofs)); return yield* registrations.registerDevice({ userId, payload }); }, mapRelayCommonApiErrors("invalid_dpop")), ) .handle( "registerLiveActivity", Effect.fn("relay.api.mobile.registerLiveActivity")(function* (args) { const { payload } = args; const { userId, token } = yield* RelayClientPrincipal; const proofKeyThumbprint = yield* requireDpopPrincipalScope("mobile:registration"); yield* requireDpopThumbprint(proofKeyThumbprint, { expectedAccessToken: token, }).pipe(Effect.provideService(DpopProofs.DpopProofReplay, dpopProofs)); return yield* registrations.registerLiveActivity({ userId, payload }); }, mapRelayCommonApiErrors("invalid_dpop")), ) .handle( "unregisterDevice", Effect.fn("relay.api.mobile.unregisterDevice")(function* (args) { const { params } = args; const { userId, token } = yield* RelayClientPrincipal; const proofKeyThumbprint = yield* requireDpopPrincipalScope("mobile:registration"); yield* requireDpopThumbprint(proofKeyThumbprint, { expectedAccessToken: token, }).pipe(Effect.provideService(DpopProofs.DpopProofReplay, dpopProofs)); return yield* registrations.unregisterDevice({ userId, deviceId: params.deviceId }); }, mapRelayCommonApiErrors("invalid_dpop")), ); }), ); export const clientApi = HttpApiBuilder.group( RelayApi, "client", Effect.fnUntraced(function* (handlers) { const config = yield* RelayConfiguration.RelayConfiguration; const crypto = yield* Crypto.Crypto; const relayTokens = yield* RelayTokens.RelayTokens; const linker = yield* EnvironmentLinker.EnvironmentLinker; const links = yield* EnvironmentLinks.EnvironmentLinks; const managedEndpointProvider = yield* ManagedEndpointProvider.ManagedEndpointProvider; const credentials = yield* EnvironmentCredentials.EnvironmentCredentials; const devices = yield* Devices.Devices; return handlers .handle( "listEnvironments", Effect.fn("relay.api.client.listEnvironments")(function* () { const { userId } = yield* RelayClientPrincipal; const environments = yield* links.listForUser({ userId }); return { environments }; }, mapRelayCommonApiErrors("not_authorized")), ) .handle( "listDevices", Effect.fn("relay.api.client.listDevices")(function* () { const { userId } = yield* RelayClientPrincipal; return { devices: yield* devices.listForUser({ userId }) }; }, mapRelayCommonApiErrors("not_authorized")), ) .handle( "linkEnvironment", Effect.fn("relay.api.client.linkEnvironment")( function* (args) { const { payload } = args; yield* appendRelayCredentialResponseHeaders; const { userId } = yield* RelayClientPrincipal; const result = yield* linker.link({ userId, request: payload }); return { ok: true, cloudUserId: userId, environmentId: result.environmentId, endpoint: result.endpoint, endpointRuntime: result.endpointRuntime, relayIssuer: config.relayIssuer, environmentCredential: result.environmentCredential, cloudMintPublicKey: config.cloudMintPublicKey, }; }, mapErrorTags({ EnvironmentLinkProofExpired: (_error, traceId) => new RelayEnvironmentLinkProofExpiredError({ code: "environment_link_proof_expired", traceId, }), EnvironmentLinkProofInvalid: (linkError, traceId) => new RelayEnvironmentLinkProofInvalidError({ code: "environment_link_proof_invalid", reason: linkError.reason, traceId, }), ManagedEndpointProvisioningNotConfigured: (_error, traceId) => new RelayEnvironmentLinkUnavailableError({ code: "environment_link_unavailable", reason: "managed_endpoint_not_configured", traceId, }), ManagedEndpointProvisioningFailed: (_error, traceId) => new RelayEnvironmentLinkUnavailableError({ code: "environment_link_unavailable", reason: "managed_endpoint_provisioning_failed", traceId, }), ManagedEndpointOriginNotAllowed: (_error, traceId) => new RelayEnvironmentLinkProofInvalidError({ code: "environment_link_proof_invalid", reason: "origin_not_allowed", traceId, }), EnvironmentLinkUpsertPersistenceError: (_error, traceId) => new RelayEnvironmentLinkFailedError({ code: "environment_link_failed", reason: "link_persistence_failed", traceId, }), EnvironmentCredentialCreatePersistenceError: (_error, traceId) => new RelayEnvironmentLinkFailedError({ code: "environment_link_failed", reason: "credential_persistence_failed", traceId, }), DpopProofReplayPersistenceError: (_error, traceId) => new RelayEnvironmentLinkFailedError({ code: "environment_link_failed",