import * as Context from "effect/Context"; import type * as DateTime from "effect/DateTime"; import * as Schema from "effect/Schema"; import * as HttpApi from "effect/unstable/httpapi/HttpApi"; import * as HttpApiEndpoint from "effect/unstable/httpapi/HttpApiEndpoint"; import * as HttpApiGroup from "effect/unstable/httpapi/HttpApiGroup"; import * as HttpApiMiddleware from "effect/unstable/httpapi/HttpApiMiddleware"; import * as HttpServerRespondable from "effect/unstable/http/HttpServerRespondable"; import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse"; import { AuthAccessTokenResult, AuthBrowserSessionRequest, AuthBrowserSessionResult, AuthClientSession, AuthCreatePairingCredentialInput, AuthPairingCredentialResult, AuthPairingLink, AuthRevokeClientSessionInput, AuthRevokePairingLinkInput, AuthEnvironmentScope, AuthTokenExchangeRequest, AuthSessionState, AuthWebSocketTicketResult, ServerAuthSessionMethod, } from "./auth.ts"; import { AuthSessionId, TrimmedNonEmptyString } from "./baseSchemas.ts"; import { ExecutionEnvironmentDescriptor } from "./environment.ts"; import { ClientOrchestrationCommand, DispatchResult, OrchestrationReadModel, } from "./orchestration.ts"; const OptionalBearerHeaders = Schema.Struct({ authorization: Schema.optionalKey(Schema.String), }); export const EnvironmentRequestInvalidReason = Schema.Literals([ "invalid_scope", "scope_not_granted", "invalid_command", ]); export type EnvironmentRequestInvalidReason = typeof EnvironmentRequestInvalidReason.Type; export const EnvironmentAuthInvalidReason = Schema.Literals([ "missing_credential", "invalid_credential", ]); export type EnvironmentAuthInvalidReason = typeof EnvironmentAuthInvalidReason.Type; export const EnvironmentOperationForbiddenReason = Schema.Literals([ "current_session_revoke_not_allowed", ]); export type EnvironmentOperationForbiddenReason = typeof EnvironmentOperationForbiddenReason.Type; export const EnvironmentInternalErrorReason = Schema.Literals([ "bootstrap_validation_failed", "browser_session_issuance_failed", "browser_session_cookie_failed", "access_token_issuance_failed", "websocket_ticket_issuance_failed", "pairing_credential_issuance_failed", "pairing_links_load_failed", "pairing_link_revoke_failed", "client_sessions_load_failed", "client_session_revoke_failed", "orchestration_snapshot_failed", "orchestration_dispatch_failed", "internal_error", ]); export type EnvironmentInternalErrorReason = typeof EnvironmentInternalErrorReason.Type; export class EnvironmentRequestInvalidError extends Schema.TaggedErrorClass()( "EnvironmentRequestInvalidError", { code: Schema.Literal("invalid_request"), reason: EnvironmentRequestInvalidReason, traceId: TrimmedNonEmptyString, }, { httpApiStatus: 400 }, ) { [HttpServerRespondable.symbol]() { return HttpServerResponse.schemaJson(EnvironmentRequestInvalidError)(this, { status: 400 }); } } export class EnvironmentAuthInvalidError extends Schema.TaggedErrorClass()( "EnvironmentAuthInvalidError", { code: Schema.Literal("auth_invalid"), reason: EnvironmentAuthInvalidReason, traceId: TrimmedNonEmptyString, }, { httpApiStatus: 401 }, ) { [HttpServerRespondable.symbol]() { return HttpServerResponse.schemaJson(EnvironmentAuthInvalidError)(this, { status: 401 }); } } export class EnvironmentScopeRequiredError extends Schema.TaggedErrorClass()( "EnvironmentScopeRequiredError", { code: Schema.Literal("insufficient_scope"), requiredScope: AuthEnvironmentScope, traceId: TrimmedNonEmptyString, }, { httpApiStatus: 403 }, ) { [HttpServerRespondable.symbol]() { return HttpServerResponse.schemaJson(EnvironmentScopeRequiredError)(this, { status: 403 }); } } export class EnvironmentOperationForbiddenError extends Schema.TaggedErrorClass()( "EnvironmentOperationForbiddenError", { code: Schema.Literal("operation_forbidden"), reason: EnvironmentOperationForbiddenReason, traceId: TrimmedNonEmptyString, }, { httpApiStatus: 403 }, ) { [HttpServerRespondable.symbol]() { return HttpServerResponse.schemaJson(EnvironmentOperationForbiddenError)(this, { status: 403 }); } } export class EnvironmentInternalError extends Schema.TaggedErrorClass()( "EnvironmentInternalError", { code: Schema.Literal("internal_error"), reason: EnvironmentInternalErrorReason, traceId: TrimmedNonEmptyString, }, { httpApiStatus: 500 }, ) { [HttpServerRespondable.symbol]() { return HttpServerResponse.schemaJson(EnvironmentInternalError)(this, { status: 500 }); } } export const EnvironmentHttpCommonError = Schema.Union([ EnvironmentRequestInvalidError, EnvironmentAuthInvalidError, EnvironmentScopeRequiredError, EnvironmentOperationForbiddenError, EnvironmentInternalError, ]); export type EnvironmentHttpCommonError = typeof EnvironmentHttpCommonError.Type; const EnvironmentAuthenticationErrors = [ EnvironmentAuthInvalidError, EnvironmentInternalError, ] as const; const EnvironmentSessionCreationErrors = [ EnvironmentAuthInvalidError, EnvironmentInternalError, ] as const; const EnvironmentTokenExchangeErrors = [ EnvironmentRequestInvalidError, EnvironmentAuthInvalidError, EnvironmentInternalError, ] as const; const EnvironmentScopedOperationErrors = [ EnvironmentScopeRequiredError, EnvironmentInternalError, ] as const; const EnvironmentPairingCredentialErrors = [ EnvironmentRequestInvalidError, ...EnvironmentScopedOperationErrors, ] as const; const EnvironmentSessionRevokeErrors = [ EnvironmentScopeRequiredError, EnvironmentOperationForbiddenError, EnvironmentInternalError, ] as const; const EnvironmentOrchestrationSnapshotErrors = [ EnvironmentScopeRequiredError, EnvironmentInternalError, ] as const; const EnvironmentOrchestrationDispatchErrors = [ EnvironmentRequestInvalidError, EnvironmentScopeRequiredError, EnvironmentInternalError, ] as const; export interface EnvironmentSessionPrincipalShape { readonly sessionId: AuthSessionId; readonly subject: string; readonly method: ServerAuthSessionMethod; readonly scopes: ReadonlySet; readonly expiresAt?: DateTime.DateTime; } export class EnvironmentAuthenticatedPrincipal extends Context.Service< EnvironmentAuthenticatedPrincipal, EnvironmentSessionPrincipalShape >()("@t3tools/contracts/environmentHttp/EnvironmentAuthenticatedPrincipal") {} export class EnvironmentAuthenticatedAuth extends HttpApiMiddleware.Service< EnvironmentAuthenticatedAuth, { provides: EnvironmentAuthenticatedPrincipal } >()("EnvironmentAuthenticatedAuth", { error: EnvironmentAuthenticationErrors, }) {} export const AuthPairingLinkRevokeResult = Schema.Struct({ revoked: Schema.Boolean, }); export type AuthPairingLinkRevokeResult = typeof AuthPairingLinkRevokeResult.Type; export const AuthClientSessionRevokeResult = Schema.Struct({ revoked: Schema.Boolean, }); export type AuthClientSessionRevokeResult = typeof AuthClientSessionRevokeResult.Type; export const AuthOtherClientSessionsRevokeResult = Schema.Struct({ revokedCount: Schema.Number, }); export type AuthOtherClientSessionsRevokeResult = typeof AuthOtherClientSessionsRevokeResult.Type; export class EnvironmentMetadataHttpApi extends HttpApiGroup.make("metadata").add( HttpApiEndpoint.get("descriptor", "/.well-known/t3/environment", { success: ExecutionEnvironmentDescriptor, }), ) {} export class EnvironmentAuthHttpApi extends HttpApiGroup.make("auth") .add( HttpApiEndpoint.get("session", "/api/auth/session", { headers: OptionalBearerHeaders, success: AuthSessionState, error: [EnvironmentInternalError], }), ) .add( HttpApiEndpoint.post("browserSession", "/api/auth/browser-session", { payload: AuthBrowserSessionRequest, success: AuthBrowserSessionResult, error: EnvironmentSessionCreationErrors, }), ) .add( HttpApiEndpoint.post("token", "/oauth/token", { payload: AuthTokenExchangeRequest, success: AuthAccessTokenResult, error: EnvironmentTokenExchangeErrors, }), ) .add( HttpApiEndpoint.post("webSocketTicket", "/api/auth/websocket-ticket", { headers: OptionalBearerHeaders, success: AuthWebSocketTicketResult, error: [EnvironmentInternalError], }).middleware(EnvironmentAuthenticatedAuth), ) .add( HttpApiEndpoint.post("pairingCredential", "/api/auth/pairing-token", { headers: OptionalBearerHeaders, payload: AuthCreatePairingCredentialInput, success: AuthPairingCredentialResult, error: EnvironmentPairingCredentialErrors, }).middleware(EnvironmentAuthenticatedAuth), ) .add( HttpApiEndpoint.get("pairingLinks", "/api/auth/pairing-links", { headers: OptionalBearerHeaders, success: Schema.Array(AuthPairingLink), error: EnvironmentScopedOperationErrors, }).middleware(EnvironmentAuthenticatedAuth), ) .add( HttpApiEndpoint.post("revokePairingLink", "/api/auth/pairing-links/revoke", { headers: OptionalBearerHeaders, payload: AuthRevokePairingLinkInput, success: AuthPairingLinkRevokeResult, error: EnvironmentScopedOperationErrors, }).middleware(EnvironmentAuthenticatedAuth), ) .add( HttpApiEndpoint.get("clients", "/api/auth/clients", { headers: OptionalBearerHeaders, success: Schema.Array(AuthClientSession), error: EnvironmentScopedOperationErrors, }).middleware(EnvironmentAuthenticatedAuth), ) .add( HttpApiEndpoint.post("revokeClient", "/api/auth/clients/revoke", { headers: OptionalBearerHeaders, payload: AuthRevokeClientSessionInput, success: AuthClientSessionRevokeResult, error: EnvironmentSessionRevokeErrors, }).middleware(EnvironmentAuthenticatedAuth), ) .add( HttpApiEndpoint.post("revokeOtherClients", "/api/auth/clients/revoke-others", { headers: OptionalBearerHeaders, success: AuthOtherClientSessionsRevokeResult, error: EnvironmentScopedOperationErrors, }).middleware(EnvironmentAuthenticatedAuth), ) {} export class EnvironmentOrchestrationHttpApi extends HttpApiGroup.make("orchestration") .add( HttpApiEndpoint.get("snapshot", "/api/orchestration/snapshot", { headers: OptionalBearerHeaders, success: OrchestrationReadModel, error: EnvironmentOrchestrationSnapshotErrors, }).middleware(EnvironmentAuthenticatedAuth), ) .add( HttpApiEndpoint.post("dispatch", "/api/orchestration/dispatch", { headers: OptionalBearerHeaders, payload: ClientOrchestrationCommand, success: DispatchResult, error: EnvironmentOrchestrationDispatchErrors, }).middleware(EnvironmentAuthenticatedAuth), ) {} export class EnvironmentHttpApi extends HttpApi.make("environment") .add(EnvironmentMetadataHttpApi) .add(EnvironmentAuthHttpApi) .add(EnvironmentOrchestrationHttpApi) {}