import { AuthAccessReadScope, AuthAccessWriteScope, AuthStandardClientScopes, AuthOrchestrationOperateScope, AuthOrchestrationReadScope, AuthRelayReadScope, AuthRelayWriteScope, AuthReviewWriteScope, AuthTerminalOperateScope, EnvironmentAuthInvalidError, type EnvironmentAuthInvalidReason, EnvironmentHttpApi, EnvironmentInternalError, type EnvironmentInternalErrorReason, EnvironmentOperationForbiddenError, EnvironmentRequestInvalidError, type EnvironmentRequestInvalidReason, EnvironmentResourceNotFoundError, type EnvironmentResourceNotFoundReason, EnvironmentScopeRequiredError, EnvironmentAuthenticatedAuth, EnvironmentAuthenticatedPrincipal,} from "@t3tools/contracts";import type { AuthEnvironmentScope, DpopFailureReason } from "@t3tools/contracts";import { parseAllowedOAuthScope } from "@t3tools/shared/oauthScope";import { causeErrorTag } from "@t3tools/shared/observability";import * as DateTime from "effect/DateTime";import * as Effect from "effect/Effect";import { identity } from "effect/Function";import * as Layer from "effect/Layer";import * as Cookies from "effect/unstable/http/Cookies";import * as HttpEffect from "effect/unstable/http/HttpEffect";import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http";import * as HttpApiBuilder from "effect/unstable/httpapi/HttpApiBuilder"; import * as EnvironmentAuth from "./EnvironmentAuth.ts";import * as SessionStore from "./SessionStore.ts";import { traceAuthenticatedRelayRequest, traceRelayRequest } from "../cloud/traceRelayRequest.ts";import { deriveAuthClientMetadata } from "./utils.ts";import { verifyRequestDpopProof } from "./dpop.ts"; const CREDENTIAL_RESPONSE_HEADERS = { "cache-control": "no-store", pragma: "no-cache",} as const; const appendCredentialResponseHeaders = HttpEffect.appendPreResponseHandler((_request, response) => Effect.succeed(HttpServerResponse.setHeaders(response, CREDENTIAL_RESPONSE_HEADERS)),); const appendDpopChallengeHeader = HttpEffect.appendPreResponseHandler((_request, response) => Effect.succeed(HttpServerResponse.setHeader(response, "www-authenticate", "DPoP")),); const appendDpopChallengeOnUnauthorized = (error: EnvironmentAuthInvalidError) => Effect.gen(function* () { const request = yield* HttpServerRequest.HttpServerRequest; const usesDpop = (request.originalUrl.startsWith("/oauth/token") && request.headers.dpop !== undefined) || request.headers.authorization?.startsWith("DPoP ") === true; if (usesDpop) { yield* appendDpopChallengeHeader; } return yield* error; }); const currentEnvironmentTraceId = Effect.currentParentSpan.pipe( Effect.map((span) => span.traceId), Effect.orElseSucceed(() => "unavailable"),); export function annotateEnvironmentRequest(endpoint: string) { return Effect.gen(function* () { const request = yield* HttpServerRequest.HttpServerRequest; const url = HttpServerRequest.toURL(request); const traceId = yield* currentEnvironmentTraceId; yield* Effect.addFinalizer((exit) => exit._tag === "Failure" ? Effect.logWarning("environment api request failed", { endpoint, traceId, errorTag: causeErrorTag(exit.cause), cause: exit.cause, }) : Effect.void, ); yield* Effect.annotateLogsScoped({ "environment.endpoint": endpoint, traceId }); yield* Effect.annotateCurrentSpan({ "environment.endpoint": endpoint, "http.request.method": request.method, "url.path": url._tag === "Some" ? url.value.pathname : "unknown", }); });} export function failEnvironmentAuthInvalid( reason: EnvironmentAuthInvalidReason, dpopFailureReason?: DpopFailureReason,) { return currentEnvironmentTraceId.pipe( Effect.flatMap((traceId) => Effect.fail( new EnvironmentAuthInvalidError({ code: "auth_invalid", reason, ...(dpopFailureReason === undefined ? {} : { dpopFailureReason }), traceId, }), ), ), );} export function failEnvironmentInvalidRequest(reason: EnvironmentRequestInvalidReason) { return currentEnvironmentTraceId.pipe( Effect.flatMap((traceId) => Effect.fail(new EnvironmentRequestInvalidError({ code: "invalid_request", reason, traceId })), ), );} export function failEnvironmentScopeRequired(requiredScope: AuthEnvironmentScope) { return currentEnvironmentTraceId.pipe( Effect.flatMap((traceId) => Effect.fail( new EnvironmentScopeRequiredError({ code: "insufficient_scope", requiredScope, traceId, }), ), ), );} function failEnvironmentOperationForbidden(reason: "current_session_revoke_not_allowed") { return currentEnvironmentTraceId.pipe( Effect.flatMap((traceId) => Effect.fail( new EnvironmentOperationForbiddenError({ code: "operation_forbidden", reason, traceId, }), ), ), );} export function failEnvironmentNotFound(reason: EnvironmentResourceNotFoundReason) { return currentEnvironmentTraceId.pipe( Effect.flatMap((traceId) => Effect.fail(new EnvironmentResourceNotFoundError({ code: "not_found", reason, traceId })), ), );} export function failEnvironmentInternal(reason: EnvironmentInternalErrorReason, error?: unknown) { return Effect.gen(function* () { const traceId = yield* currentEnvironmentTraceId; if (error !== undefined) { yield* Effect.logError("environment api operation failed", { reason, traceId, cause: error, }); } return yield* new EnvironmentInternalError({ code: "internal_error", reason, traceId }); });} const appendSessionCookie = (cookieName: string, token: string, expiresAt: DateTime.DateTime) => Effect.fromResult( Cookies.set(Cookies.empty, cookieName, token, { expires: DateTime.toDate(expiresAt), httpOnly: true, path: "/", sameSite: "lax", }), ).pipe( Effect.catch(() => failEnvironmentInternal("browser_session_cookie_failed")), Effect.flatMap((cookies) => HttpEffect.appendPreResponseHandler((_request, response) => Effect.succeed(HttpServerResponse.mergeCookies(response, cookies)), ), ), ); export const requireEnvironmentScope = Effect.fn("environment.auth.requireScope")(function* ( scope: AuthEnvironmentScope,) { const session = yield* EnvironmentAuthenticatedPrincipal; if (!session.scopes.has(scope)) { return yield* failEnvironmentScopeRequired(scope); } return session;}); export const environmentAuthenticatedAuthLayer = Layer.effect( EnvironmentAuthenticatedAuth, Effect.gen(function* () { const serverAuth = yield* EnvironmentAuth.EnvironmentAuth; return (httpEffect) => Effect.gen(function* () { const request = yield* HttpServerRequest.HttpServerRequest; const session = yield* serverAuth.authenticateHttpRequest(request).pipe( Effect.catchIf(EnvironmentAuth.isServerAuthCredentialError, (error) => failEnvironmentAuthInvalid( EnvironmentAuth.serverAuthCredentialReason(error), EnvironmentAuth.serverAuthDpopFailureReason(error), ), ), Effect.catchIf(EnvironmentAuth.isServerAuthInternalError, (error) => failEnvironmentInternal("internal_error", error), ), ); return yield* httpEffect.pipe( Effect.provideService(EnvironmentAuthenticatedPrincipal, { ...session, scopes: new Set(session.scopes), }), session.subject === "cloud-connect" ? traceAuthenticatedRelayRequest : identity, ); }).pipe(Effect.catchTag("EnvironmentAuthInvalidError", appendDpopChallengeOnUnauthorized)); }),); export const authHttpApiLayer = HttpApiBuilder.group( EnvironmentHttpApi, "auth", Effect.fnUntraced(function* (handlers) { const serverAuth = yield* EnvironmentAuth.EnvironmentAuth; const sessions = yield* SessionStore.SessionStore; return handlers .handle( "session", Effect.fn("environment.auth.session")( function* (args) { yield* annotateEnvironmentRequest(args.endpoint.name); const request = yield* HttpServerRequest.HttpServerRequest; const result = yield* serverAuth.getSessionState(request); const credential = EnvironmentAuth.selectRequestCredential( request, sessions.cookieName, sessions.legacyCookieName, ); if ( credential?.source === "legacy-cookie" && result.authenticated && result.sessionMethod === "browser-session-cookie" && result.expiresAt ) { yield* appendSessionCookie(sessions.cookieName, credential.token, result.expiresAt); yield* appendCredentialResponseHeaders; } return result; }, Effect.catchIf(EnvironmentAuth.isServerAuthInternalError, (error) => failEnvironmentInternal("internal_error", error), ), ), ) .handle( "browserSession", Effect.fn("environment.auth.browserSession")( function* (args) { yield* annotateEnvironmentRequest(args.endpoint.name); const request = yield* HttpServerRequest.HttpServerRequest; const result = yield* serverAuth.createBrowserSession( args.payload.credential, deriveAuthClientMetadata({ request }), ); yield* appendSessionCookie( sessions.cookieName, result.sessionToken, result.response.expiresAt, ); yield* appendCredentialResponseHeaders; return result.response; }, Effect.catchIf(EnvironmentAuth.isServerAuthCredentialError, (error) => failEnvironmentAuthInvalid( EnvironmentAuth.serverAuthCredentialReason(error), EnvironmentAuth.serverAuthDpopFailureReason(error), ), ), Effect.catchIf(EnvironmentAuth.isServerAuthInternalError, (error) => failEnvironmentInternal("browser_session_issuance_failed", error), ), ), ) .handle( "token", Effect.fn("environment.auth.token")( function* (args) { yield* annotateEnvironmentRequest(args.endpoint.name); const request = yield* HttpServerRequest.HttpServerRequest; const requestedScopes = args.payload.scope === undefined ? undefined : parseAllowedOAuthScope({ value: args.payload.scope, allowedScopes: new Set<AuthEnvironmentScope>([ AuthOrchestrationReadScope, AuthOrchestrationOperateScope, AuthTerminalOperateScope, AuthReviewWriteScope, AuthAccessReadScope, AuthAccessWriteScope, AuthRelayReadScope, AuthRelayWriteScope, ]), }); if (requestedScopes === null) { return yield* failEnvironmentInvalidRequest("invalid_scope"); } const proofKeyThumbprint = args.headers.dpop ? yield* verifyRequestDpopProof({ request }).pipe( Effect.catchIf(EnvironmentAuth.isServerAuthCredentialError, (error) => appendDpopChallengeHeader.pipe( Effect.andThen( failEnvironmentAuthInvalid( "invalid_credential", EnvironmentAuth.serverAuthDpopFailureReason(error), ), ), ), ), Effect.catchIf(EnvironmentAuth.isServerAuthInternalError, (error) => failEnvironmentInternal("access_token_issuance_failed", error), ), ) : undefined; yield* appendCredentialResponseHeaders; return yield* serverAuth.exchangeBootstrapCredentialForAccessToken( args.payload.subject_token, requestedScopes, deriveAuthClientMetadata({ request, presented: { ...(args.payload.client_label ? { label: args.payload.client_label } : {}), ...(args.payload.client_device_type ? { deviceType: args.payload.client_device_type } : {}), ...(args.payload.client_os ? { os: args.payload.client_os } : {}), }, }), proofKeyThumbprint ? { proofKeyThumbprint } : undefined, ); }, traceRelayRequest, Effect.catchIf(EnvironmentAuth.isServerAuthCredentialError, (error) => failEnvironmentAuthInvalid( EnvironmentAuth.serverAuthCredentialReason(error), EnvironmentAuth.serverAuthDpopFailureReason(error), ), ), Effect.catchIf(EnvironmentAuth.isServerAuthInvalidRequestError, (error) => failEnvironmentInvalidRequest(EnvironmentAuth.serverAuthInvalidRequestReason(error)), ), Effect.catchIf(EnvironmentAuth.isServerAuthInternalError, (error) => failEnvironmentInternal("access_token_issuance_failed", error), ), ), ) .handle( "webSocketTicket", Effect.fn("environment.auth.webSocketTicket")( function* (args) { yield* annotateEnvironmentRequest(args.endpoint.name); const session = yield* EnvironmentAuthenticatedPrincipal; yield* appendCredentialResponseHeaders; return yield* serverAuth.issueWebSocketTicket(session); }, Effect.catchIf(EnvironmentAuth.isServerAuthInternalError, (error) => failEnvironmentInternal("websocket_ticket_issuance_failed", error), ), ), ) .handle( "pairingCredential", Effect.fn("environment.auth.pairingCredential")( function* (args) { yield* annotateEnvironmentRequest(args.endpoint.name); const session = yield* requireEnvironmentScope(AuthAccessWriteScope); const delegatedScopes = args.payload.scopes ?? AuthStandardClientScopes; if ( delegatedScopes.length === 0 || new Set<AuthEnvironmentScope>(delegatedScopes).size !== delegatedScopes.length ) { return yield* failEnvironmentInvalidRequest("invalid_scope"); } for (const delegatedScope of delegatedScopes) { if (!session.scopes.has(delegatedScope)) { return yield* failEnvironmentScopeRequired(delegatedScope); } } return yield* serverAuth.issuePairingCredential(args.payload); }, Effect.catchIf(EnvironmentAuth.isServerAuthInternalError, (error) => failEnvironmentInternal("pairing_credential_issuance_failed", error), ), ), ) .handle( "pairingLinks", Effect.fn("environment.auth.pairingLinks")( function* (args) { yield* annotateEnvironmentRequest(args.endpoint.name); yield* requireEnvironmentScope(AuthAccessReadScope); return yield* serverAuth.listPairingLinks(); }, Effect.catchIf(EnvironmentAuth.isServerAuthInternalError, (error) => failEnvironmentInternal("pairing_links_load_failed", error), ), ), ) .handle( "revokePairingLink", Effect.fn("environment.auth.revokePairingLink")( function* (args) { yield* annotateEnvironmentRequest(args.endpoint.name); yield* requireEnvironmentScope(AuthAccessWriteScope); const revoked = yield* serverAuth.revokePairingLink(args.payload.id); return { revoked }; }, Effect.catchIf(EnvironmentAuth.isServerAuthInternalError, (error) => failEnvironmentInternal("pairing_link_revoke_failed", error), ), ), ) .handle( "clients", Effect.fn("environment.auth.clients")( function* (args) { yield* annotateEnvironmentRequest(args.endpoint.name); const session = yield* requireEnvironmentScope(AuthAccessReadScope); return yield* serverAuth.listClientSessions(session.sessionId); }, Effect.catchIf(EnvironmentAuth.isServerAuthInternalError, (error) => failEnvironmentInternal("client_sessions_load_failed", error), ), ), ) .handle( "revokeClient", Effect.fn("environment.auth.revokeClient")( function* (args) { yield* annotateEnvironmentRequest(args.endpoint.name); const session = yield* requireEnvironmentScope(AuthAccessWriteScope); const revoked = yield* serverAuth.revokeClientSession( session.sessionId, args.payload.sessionId, ); return { revoked }; }, Effect.catchTag("ServerAuthForbiddenOperationError", () => failEnvironmentOperationForbidden("current_session_revoke_not_allowed"), ), Effect.catchIf(EnvironmentAuth.isServerAuthInternalError, (error) => failEnvironmentInternal("client_session_revoke_failed", error), ), ), ) .handle( "revokeOtherClients", Effect.fn("environment.auth.revokeOtherClients")( function* (args) { yield* annotateEnvironmentRequest(args.endpoint.name); const session = yield* requireEnvironmentScope(AuthAccessWriteScope); const revokedCount = yield* serverAuth.revokeOtherClientSessions(session.sessionId); return { revokedCount }; }, Effect.catchIf(EnvironmentAuth.isServerAuthInternalError, (error) => failEnvironmentInternal("client_session_revoke_failed", error), ), ), ); }),);
import {
AuthAccessReadScope,
AuthAccessWriteScope,
AuthStandardClientScopes,
AuthOrchestrationOperateScope,
AuthOrchestrationReadScope,
AuthRelayReadScope,
AuthRelayWriteScope,
AuthReviewWriteScope,
AuthTerminalOperateScope,
EnvironmentAuthInvalidError,
type EnvironmentAuthInvalidReason,
EnvironmentHttpApi,
EnvironmentInternalError,
type EnvironmentInternalErrorReason,
EnvironmentOperationForbiddenError,
EnvironmentRequestInvalidError,
type EnvironmentRequestInvalidReason,
EnvironmentResourceNotFoundError,
type EnvironmentResourceNotFoundReason,
EnvironmentScopeRequiredError,
EnvironmentAuthenticatedAuth,
EnvironmentAuthenticatedPrincipal,
} from "@t3tools/contracts";
import type { AuthEnvironmentScope, DpopFailureReason } from "@t3tools/contracts";
import { parseAllowedOAuthScope } from "@t3tools/shared/oauthScope";
import { causeErrorTag } from "@t3tools/shared/observability";
import * as DateTime from "effect/DateTime";
import * as Effect from "effect/Effect";
import { identity } from "effect/Function";
import * as Layer from "effect/Layer";
import * as Cookies from "effect/unstable/http/Cookies";
import * as HttpEffect from "effect/unstable/http/HttpEffect";
import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http";
import * as HttpApiBuilder from "effect/unstable/httpapi/HttpApiBuilder";
import * as EnvironmentAuth from "./EnvironmentAuth.ts";
import * as SessionStore from "./SessionStore.ts";
import { traceAuthenticatedRelayRequest, traceRelayRequest } from "../cloud/traceRelayRequest.ts";
import { deriveAuthClientMetadata } from "./utils.ts";
import { verifyRequestDpopProof } from "./dpop.ts";
const CREDENTIAL_RESPONSE_HEADERS = {
"cache-control": "no-store",
pragma: "no-cache",
} as const;
const appendCredentialResponseHeaders = HttpEffect.appendPreResponseHandler((_request, response) =>
Effect.succeed(HttpServerResponse.setHeaders(response, CREDENTIAL_RESPONSE_HEADERS)),
);
const appendDpopChallengeHeader = HttpEffect.appendPreResponseHandler((_request, response) =>
Effect.succeed(HttpServerResponse.setHeader(response, "www-authenticate", "DPoP")),
);
const appendDpopChallengeOnUnauthorized = (error: EnvironmentAuthInvalidError) =>
Effect.gen(function* () {
const request = yield* HttpServerRequest.HttpServerRequest;
const usesDpop =
(request.originalUrl.startsWith("/oauth/token") && request.headers.dpop !== undefined) ||
request.headers.authorization?.startsWith("DPoP ") === true;
if (usesDpop) {
yield* appendDpopChallengeHeader;
}
return yield* error;
});
const currentEnvironmentTraceId = Effect.currentParentSpan.pipe(
Effect.map((span) => span.traceId),
Effect.orElseSucceed(() => "unavailable"),
);
export function annotateEnvironmentRequest(endpoint: string) {
return Effect.gen(function* () {
const request = yield* HttpServerRequest.HttpServerRequest;
const url = HttpServerRequest.toURL(request);
const traceId = yield* currentEnvironmentTraceId;
yield* Effect.addFinalizer((exit) =>
exit._tag === "Failure"
? Effect.logWarning("environment api request failed", {
endpoint,
traceId,
errorTag: causeErrorTag(exit.cause),
cause: exit.cause,
})
: Effect.void,
);
yield* Effect.annotateLogsScoped({ "environment.endpoint": endpoint, traceId });
yield* Effect.annotateCurrentSpan({
"environment.endpoint": endpoint,
"http.request.method": request.method,
"url.path": url._tag === "Some" ? url.value.pathname : "unknown",
});
});
}
export function failEnvironmentAuthInvalid(
reason: EnvironmentAuthInvalidReason,
dpopFailureReason?: DpopFailureReason,
) {
return currentEnvironmentTraceId.pipe(
Effect.flatMap((traceId) =>
Effect.fail(
new EnvironmentAuthInvalidError({
code: "auth_invalid",
reason,
...(dpopFailureReason === undefined ? {} : { dpopFailureReason }),
traceId,
}),
),
),
);
}
export function failEnvironmentInvalidRequest(reason: EnvironmentRequestInvalidReason) {
return currentEnvironmentTraceId.pipe(
Effect.flatMap((traceId) =>
Effect.fail(new EnvironmentRequestInvalidError({ code: "invalid_request", reason, traceId })),
),
);
}
export function failEnvironmentScopeRequired(requiredScope: AuthEnvironmentScope) {
return currentEnvironmentTraceId.pipe(
Effect.flatMap((traceId) =>
Effect.fail(
new EnvironmentScopeRequiredError({
code: "insufficient_scope",
requiredScope,
traceId,
}),
),
),
);
}
function failEnvironmentOperationForbidden(reason: "current_session_revoke_not_allowed") {
return currentEnvironmentTraceId.pipe(
Effect.flatMap((traceId) =>
Effect.fail(
new EnvironmentOperationForbiddenError({
code: "operation_forbidden",
reason,
traceId,
}),
),
),
);
}
export function failEnvironmentNotFound(reason: EnvironmentResourceNotFoundReason) {
return currentEnvironmentTraceId.pipe(
Effect.flatMap((traceId) =>
Effect.fail(new EnvironmentResourceNotFoundError({ code: "not_found", reason, traceId })),
),
);
}
export function failEnvironmentInternal(reason: EnvironmentInternalErrorReason, error?: unknown) {
return Effect.gen(function* () {
const traceId = yield* currentEnvironmentTraceId;
if (error !== undefined) {
yield* Effect.logError("environment api operation failed", {
reason,
traceId,
cause: error,
});
}
return yield* new EnvironmentInternalError({ code: "internal_error", reason, traceId });
});
}
const appendSessionCookie = (cookieName: string, token: string, expiresAt: DateTime.DateTime) =>
Effect.fromResult(
Cookies.set(Cookies.empty, cookieName, token, {
expires: DateTime.toDate(expiresAt),
httpOnly: true,
path: "/",
sameSite: "lax",
}),
).pipe(
Effect.catch(() => failEnvironmentInternal("browser_session_cookie_failed")),
Effect.flatMap((cookies) =>
HttpEffect.appendPreResponseHandler((_request, response) =>
Effect.succeed(HttpServerResponse.mergeCookies(response, cookies)),
),
),
);
export const requireEnvironmentScope = Effect.fn("environment.auth.requireScope")(function* (
scope: AuthEnvironmentScope,
) {
const session = yield* EnvironmentAuthenticatedPrincipal;
if (!session.scopes.has(scope)) {
return yield* failEnvironmentScopeRequired(scope);
}
return session;
});
export const environmentAuthenticatedAuthLayer = Layer.effect(
EnvironmentAuthenticatedAuth,
Effect.gen(function* () {
const serverAuth = yield* EnvironmentAuth.EnvironmentAuth;
return (httpEffect) =>
Effect.gen(function* () {
const request = yield* HttpServerRequest.HttpServerRequest;
const session = yield* serverAuth.authenticateHttpRequest(request).pipe(
Effect.catchIf(EnvironmentAuth.isServerAuthCredentialError, (error) =>
failEnvironmentAuthInvalid(
EnvironmentAuth.serverAuthCredentialReason(error),
EnvironmentAuth.serverAuthDpopFailureReason(error),
),
),
Effect.catchIf(EnvironmentAuth.isServerAuthInternalError, (error) =>
failEnvironmentInternal("internal_error", error),
),
);
return yield* httpEffect.pipe(
Effect.provideService(EnvironmentAuthenticatedPrincipal, {
...session,
scopes: new Set(session.scopes),
}),
session.subject === "cloud-connect" ? traceAuthenticatedRelayRequest : identity,
);
}).pipe(Effect.catchTag("EnvironmentAuthInvalidError", appendDpopChallengeOnUnauthorized));
}),
);
export const authHttpApiLayer = HttpApiBuilder.group(
EnvironmentHttpApi,
"auth",
Effect.fnUntraced(function* (handlers) {
const serverAuth = yield* EnvironmentAuth.EnvironmentAuth;
const sessions = yield* SessionStore.SessionStore;
return handlers
.handle(
"session",
Effect.fn("environment.auth.session")(
function* (args) {
yield* annotateEnvironmentRequest(args.endpoint.name);
const request = yield* HttpServerRequest.HttpServerRequest;
const result = yield* serverAuth.getSessionState(request);
const credential = EnvironmentAuth.selectRequestCredential(
request,
sessions.cookieName,
sessions.legacyCookieName,
);
if (
credential?.source === "legacy-cookie" &&
result.authenticated &&
result.sessionMethod === "browser-session-cookie" &&
result.expiresAt
) {
yield* appendSessionCookie(sessions.cookieName, credential.token, result.expiresAt);
yield* appendCredentialResponseHeaders;
}
return result;
},
Effect.catchIf(EnvironmentAuth.isServerAuthInternalError, (error) =>
failEnvironmentInternal("internal_error", error),
),
),
)
.handle(
"browserSession",
Effect.fn("environment.auth.browserSession")(
function* (args) {
yield* annotateEnvironmentRequest(args.endpoint.name);
const request = yield* HttpServerRequest.HttpServerRequest;
const result = yield* serverAuth.createBrowserSession(
args.payload.credential,
deriveAuthClientMetadata({ request }),
);
yield* appendSessionCookie(
sessions.cookieName,
result.sessionToken,
result.response.expiresAt,
);
yield* appendCredentialResponseHeaders;
return result.response;
},
Effect.catchIf(EnvironmentAuth.isServerAuthCredentialError, (error) =>
failEnvironmentAuthInvalid(
EnvironmentAuth.serverAuthCredentialReason(error),
EnvironmentAuth.serverAuthDpopFailureReason(error),
),
),
Effect.catchIf(EnvironmentAuth.isServerAuthInternalError, (error) =>
failEnvironmentInternal("browser_session_issuance_failed", error),
),
),
)
.handle(
"token",
Effect.fn("environment.auth.token")(
function* (args) {
yield* annotateEnvironmentRequest(args.endpoint.name);
const request = yield* HttpServerRequest.HttpServerRequest;
const requestedScopes =
args.payload.scope === undefined
? undefined
: parseAllowedOAuthScope({
value: args.payload.scope,
allowedScopes: new Set<AuthEnvironmentScope>([
AuthOrchestrationReadScope,
AuthOrchestrationOperateScope,
AuthTerminalOperateScope,
AuthReviewWriteScope,
AuthAccessReadScope,
AuthAccessWriteScope,
AuthRelayReadScope,
AuthRelayWriteScope,
]),
});
if (requestedScopes === null) {
return yield* failEnvironmentInvalidRequest("invalid_scope");
}
const proofKeyThumbprint = args.headers.dpop
? yield* verifyRequestDpopProof({ request }).pipe(
Effect.catchIf(EnvironmentAuth.isServerAuthCredentialError, (error) =>
appendDpopChallengeHeader.pipe(
Effect.andThen(
failEnvironmentAuthInvalid(
"invalid_credential",
EnvironmentAuth.serverAuthDpopFailureReason(error),
),
),
),
),
Effect.catchIf(EnvironmentAuth.isServerAuthInternalError, (error) =>
failEnvironmentInternal("access_token_issuance_failed", error),
),
)
: undefined;
yield* appendCredentialResponseHeaders;
return yield* serverAuth.exchangeBootstrapCredentialForAccessToken(
args.payload.subject_token,
requestedScopes,
deriveAuthClientMetadata({
request,
presented: {
...(args.payload.client_label ? { label: args.payload.client_label } : {}),
...(args.payload.client_device_type
? { deviceType: args.payload.client_device_type }
: {}),
...(args.payload.client_os ? { os: args.payload.client_os } : {}),
},
}),
proofKeyThumbprint ? { proofKeyThumbprint } : undefined,
);
},
traceRelayRequest,
Effect.catchIf(EnvironmentAuth.isServerAuthCredentialError, (error) =>
failEnvironmentAuthInvalid(
EnvironmentAuth.serverAuthCredentialReason(error),
EnvironmentAuth.serverAuthDpopFailureReason(error),
),
),
Effect.catchIf(EnvironmentAuth.isServerAuthInvalidRequestError, (error) =>
failEnvironmentInvalidRequest(EnvironmentAuth.serverAuthInvalidRequestReason(error)),
),
Effect.catchIf(EnvironmentAuth.isServerAuthInternalError, (error) =>
failEnvironmentInternal("access_token_issuance_failed", error),
),
),
)
.handle(
"webSocketTicket",
Effect.fn("environment.auth.webSocketTicket")(
function* (args) {
yield* annotateEnvironmentRequest(args.endpoint.name);
const session = yield* EnvironmentAuthenticatedPrincipal;
yield* appendCredentialResponseHeaders;
return yield* serverAuth.issueWebSocketTicket(session);
},
Effect.catchIf(EnvironmentAuth.isServerAuthInternalError, (error) =>
failEnvironmentInternal("websocket_ticket_issuance_failed", error),
),
),
)
.handle(
"pairingCredential",
Effect.fn("environment.auth.pairingCredential")(
function* (args) {
yield* annotateEnvironmentRequest(args.endpoint.name);
const session = yield* requireEnvironmentScope(AuthAccessWriteScope);
const delegatedScopes = args.payload.scopes ?? AuthStandardClientScopes;
if (
delegatedScopes.length === 0 ||
new Set<AuthEnvironmentScope>(delegatedScopes).size !== delegatedScopes.length
) {
return yield* failEnvironmentInvalidRequest("invalid_scope");
}
for (const delegatedScope of delegatedScopes) {
if (!session.scopes.has(delegatedScope)) {
return yield* failEnvironmentScopeRequired(delegatedScope);
}
}
return yield* serverAuth.issuePairingCredential(args.payload);
},
Effect.catchIf(EnvironmentAuth.isServerAuthInternalError, (error) =>
failEnvironmentInternal("pairing_credential_issuance_failed", error),
),
),
)
.handle(
"pairingLinks",
Effect.fn("environment.auth.pairingLinks")(
function* (args) {
yield* annotateEnvironmentRequest(args.endpoint.name);
yield* requireEnvironmentScope(AuthAccessReadScope);
return yield* serverAuth.listPairingLinks();
},
Effect.catchIf(EnvironmentAuth.isServerAuthInternalError, (error) =>
failEnvironmentInternal("pairing_links_load_failed", error),
),
),
)
.handle(
"revokePairingLink",
Effect.fn("environment.auth.revokePairingLink")(
function* (args) {
yield* annotateEnvironmentRequest(args.endpoint.name);
yield* requireEnvironmentScope(AuthAccessWriteScope);
const revoked = yield* serverAuth.revokePairingLink(args.payload.id);
return { revoked };
},
Effect.catchIf(EnvironmentAuth.isServerAuthInternalError, (error) =>
failEnvironmentInternal("pairing_link_revoke_failed", error),
),
),
)
.handle(
"clients",
Effect.fn("environment.auth.clients")(
function* (args) {
yield* annotateEnvironmentRequest(args.endpoint.name);
const session = yield* requireEnvironmentScope(AuthAccessReadScope);
return yield* serverAuth.listClientSessions(session.sessionId);
},
Effect.catchIf(EnvironmentAuth.isServerAuthInternalError, (error) =>
failEnvironmentInternal("client_sessions_load_failed", error),
),
),
)
.handle(
"revokeClient",
Effect.fn("environment.auth.revokeClient")(
function* (args) {
yield* annotateEnvironmentRequest(args.endpoint.name);
const session = yield* requireEnvironmentScope(AuthAccessWriteScope);
const revoked = yield* serverAuth.revokeClientSession(
session.sessionId,
args.payload.sessionId,
);
return { revoked };
},
Effect.catchTag("ServerAuthForbiddenOperationError", () =>
failEnvironmentOperationForbidden("current_session_revoke_not_allowed"),
),
Effect.catchIf(EnvironmentAuth.isServerAuthInternalError, (error) =>
failEnvironmentInternal("client_session_revoke_failed", error),
),
),
)
.handle(
"revokeOtherClients",
Effect.fn("environment.auth.revokeOtherClients")(
function* (args) {
yield* annotateEnvironmentRequest(args.endpoint.name);
const session = yield* requireEnvironmentScope(AuthAccessWriteScope);
const revokedCount = yield* serverAuth.revokeOtherClientSessions(session.sessionId);
return { revokedCount };
},
Effect.catchIf(EnvironmentAuth.isServerAuthInternalError, (error) =>
failEnvironmentInternal("client_session_revoke_failed", error),
),
),
);
}),
);