import type { AuthClientSession, AuthPairingLink } from "@t3tools/contracts";import { DateTime, Effect, Layer } from "effect"; import { BootstrapCredentialServiceLive } from "./BootstrapCredentialService.ts";import { ServerSecretStoreLive } from "./ServerSecretStore.ts";import { SessionCredentialServiceLive } from "./SessionCredentialService.ts";import { BootstrapCredentialService } from "../Services/BootstrapCredentialService.ts";import { SessionCredentialService } from "../Services/SessionCredentialService.ts";import { layerConfig as SqlitePersistenceLayerLive } from "../../persistence/Layers/Sqlite.ts";import { AuthControlPlane, AuthControlPlaneError, DEFAULT_SESSION_SUBJECT,} from "../Services/AuthControlPlane.ts";import type { AuthControlPlaneShape, IssuedBearerSession, IssuedPairingLink,} from "../Services/AuthControlPlane.ts"; const bySessionPriority = (left: AuthClientSession, right: AuthClientSession) => { if (left.role !== right.role) { return left.role === "owner" ? -1 : 1; } if (left.connected !== right.connected) { return left.connected ? -1 : 1; } return right.issuedAt.epochMilliseconds - left.issuedAt.epochMilliseconds;}; const toAuthControlPlaneError = (message: string) => (cause: unknown): AuthControlPlaneError => new AuthControlPlaneError({ message, cause, }); export const makeAuthControlPlane = Effect.gen(function* () { const bootstrapCredentials = yield* BootstrapCredentialService; const sessions = yield* SessionCredentialService; const createPairingLink: AuthControlPlaneShape["createPairingLink"] = (input) => Effect.gen(function* () { const createdAt = yield* DateTime.now; const issued = yield* bootstrapCredentials.issueOneTimeToken({ role: input?.role ?? "client", subject: input?.subject ?? "one-time-token", ...(input?.ttl ? { ttl: input.ttl } : {}), ...(input?.label ? { label: input.label } : {}), }); return { id: issued.id, credential: issued.credential, role: input?.role ?? "client", subject: input?.subject ?? "one-time-token", ...(issued.label ? { label: issued.label } : {}), createdAt: DateTime.toUtc(createdAt), expiresAt: DateTime.toUtc(issued.expiresAt), } satisfies IssuedPairingLink; }).pipe(Effect.mapError(toAuthControlPlaneError("Failed to create pairing link."))); const listPairingLinks: AuthControlPlaneShape["listPairingLinks"] = (input) => bootstrapCredentials.listActive().pipe( Effect.map((pairingLinks) => pairingLinks .filter((pairingLink) => (input?.role ? pairingLink.role === input.role : true)) .filter((pairingLink) => !input?.excludeSubjects?.includes(pairingLink.subject)) .map((pairingLink) => pairingLink.label ? ({ id: pairingLink.id, credential: pairingLink.credential, role: pairingLink.role, subject: pairingLink.subject, label: pairingLink.label, createdAt: pairingLink.createdAt, expiresAt: pairingLink.expiresAt, } satisfies AuthPairingLink) : ({ id: pairingLink.id, credential: pairingLink.credential, role: pairingLink.role, subject: pairingLink.subject, createdAt: pairingLink.createdAt, expiresAt: pairingLink.expiresAt, } satisfies AuthPairingLink), ) .toSorted( (left, right) => right.createdAt.epochMilliseconds - left.createdAt.epochMilliseconds, ), ), Effect.mapError(toAuthControlPlaneError("Failed to list pairing links.")), ); const revokePairingLink: AuthControlPlaneShape["revokePairingLink"] = (id) => bootstrapCredentials .revoke(id) .pipe(Effect.mapError(toAuthControlPlaneError("Failed to revoke pairing link."))); const issueSession: AuthControlPlaneShape["issueSession"] = (input) => sessions .issue({ subject: input?.subject ?? DEFAULT_SESSION_SUBJECT, method: "bearer-session-token", role: input?.role ?? "owner", client: { ...(input?.label ? { label: input.label } : {}), deviceType: "bot", }, ...(input?.ttl ? { ttl: input.ttl } : {}), }) .pipe( Effect.flatMap((issued) => { if (issued.method !== "bearer-session-token") { return Effect.fail( new AuthControlPlaneError({ message: "CLI session issuance produced an unexpected session method.", }), ); } return Effect.succeed({ sessionId: issued.sessionId, token: issued.token, method: "bearer-session-token" as const, role: issued.role, subject: input?.subject ?? DEFAULT_SESSION_SUBJECT, client: issued.client, expiresAt: DateTime.toUtc(issued.expiresAt), } satisfies IssuedBearerSession); }), Effect.mapError(toAuthControlPlaneError("Failed to issue session token.")), ); const listSessions: AuthControlPlaneShape["listSessions"] = () => sessions.listActive().pipe( Effect.map((activeSessions) => activeSessions.toSorted(bySessionPriority)), Effect.mapError(toAuthControlPlaneError("Failed to list sessions.")), ); const revokeSession: AuthControlPlaneShape["revokeSession"] = (sessionId) => sessions .revoke(sessionId) .pipe(Effect.mapError(toAuthControlPlaneError("Failed to revoke session."))); const revokeOtherSessionsExcept: AuthControlPlaneShape["revokeOtherSessionsExcept"] = ( sessionId, ) => sessions .revokeAllExcept(sessionId) .pipe(Effect.mapError(toAuthControlPlaneError("Failed to revoke other sessions."))); return { createPairingLink, listPairingLinks, revokePairingLink, issueSession, listSessions, revokeSession, revokeOtherSessionsExcept, } satisfies AuthControlPlaneShape;}); export const AuthCoreLive = Layer.mergeAll( BootstrapCredentialServiceLive, SessionCredentialServiceLive,); export const AuthStorageLive = Layer.mergeAll(ServerSecretStoreLive, SqlitePersistenceLayerLive); export const AuthRuntimeLive = AuthCoreLive.pipe(Layer.provideMerge(AuthStorageLive)); export const AuthControlPlaneLive = Layer.effect(AuthControlPlane, makeAuthControlPlane); export const AuthControlPlaneRuntimeLive = AuthControlPlaneLive.pipe( Layer.provideMerge(AuthRuntimeLive),);
import type { AuthClientSession, AuthPairingLink } from "@t3tools/contracts";
import { DateTime, Effect, Layer } from "effect";
import { BootstrapCredentialServiceLive } from "./BootstrapCredentialService.ts";
import { ServerSecretStoreLive } from "./ServerSecretStore.ts";
import { SessionCredentialServiceLive } from "./SessionCredentialService.ts";
import { BootstrapCredentialService } from "../Services/BootstrapCredentialService.ts";
import { SessionCredentialService } from "../Services/SessionCredentialService.ts";
import { layerConfig as SqlitePersistenceLayerLive } from "../../persistence/Layers/Sqlite.ts";
import {
AuthControlPlane,
AuthControlPlaneError,
DEFAULT_SESSION_SUBJECT,
} from "../Services/AuthControlPlane.ts";
import type {
AuthControlPlaneShape,
IssuedBearerSession,
IssuedPairingLink,
} from "../Services/AuthControlPlane.ts";
const bySessionPriority = (left: AuthClientSession, right: AuthClientSession) => {
if (left.role !== right.role) {
return left.role === "owner" ? -1 : 1;
}
if (left.connected !== right.connected) {
return left.connected ? -1 : 1;
}
return right.issuedAt.epochMilliseconds - left.issuedAt.epochMilliseconds;
};
const toAuthControlPlaneError =
(message: string) =>
(cause: unknown): AuthControlPlaneError =>
new AuthControlPlaneError({
message,
cause,
});
export const makeAuthControlPlane = Effect.gen(function* () {
const bootstrapCredentials = yield* BootstrapCredentialService;
const sessions = yield* SessionCredentialService;
const createPairingLink: AuthControlPlaneShape["createPairingLink"] = (input) =>
Effect.gen(function* () {
const createdAt = yield* DateTime.now;
const issued = yield* bootstrapCredentials.issueOneTimeToken({
role: input?.role ?? "client",
subject: input?.subject ?? "one-time-token",
...(input?.ttl ? { ttl: input.ttl } : {}),
...(input?.label ? { label: input.label } : {}),
});
return {
id: issued.id,
credential: issued.credential,
role: input?.role ?? "client",
subject: input?.subject ?? "one-time-token",
...(issued.label ? { label: issued.label } : {}),
createdAt: DateTime.toUtc(createdAt),
expiresAt: DateTime.toUtc(issued.expiresAt),
} satisfies IssuedPairingLink;
}).pipe(Effect.mapError(toAuthControlPlaneError("Failed to create pairing link.")));
const listPairingLinks: AuthControlPlaneShape["listPairingLinks"] = (input) =>
bootstrapCredentials.listActive().pipe(
Effect.map((pairingLinks) =>
pairingLinks
.filter((pairingLink) => (input?.role ? pairingLink.role === input.role : true))
.filter((pairingLink) => !input?.excludeSubjects?.includes(pairingLink.subject))
.map((pairingLink) =>
pairingLink.label
? ({
id: pairingLink.id,
credential: pairingLink.credential,
role: pairingLink.role,
subject: pairingLink.subject,
label: pairingLink.label,
createdAt: pairingLink.createdAt,
expiresAt: pairingLink.expiresAt,
} satisfies AuthPairingLink)
: ({
id: pairingLink.id,
credential: pairingLink.credential,
role: pairingLink.role,
subject: pairingLink.subject,
createdAt: pairingLink.createdAt,
expiresAt: pairingLink.expiresAt,
} satisfies AuthPairingLink),
)
.toSorted(
(left, right) => right.createdAt.epochMilliseconds - left.createdAt.epochMilliseconds,
),
),
Effect.mapError(toAuthControlPlaneError("Failed to list pairing links.")),
);
const revokePairingLink: AuthControlPlaneShape["revokePairingLink"] = (id) =>
bootstrapCredentials
.revoke(id)
.pipe(Effect.mapError(toAuthControlPlaneError("Failed to revoke pairing link.")));
const issueSession: AuthControlPlaneShape["issueSession"] = (input) =>
sessions
.issue({
subject: input?.subject ?? DEFAULT_SESSION_SUBJECT,
method: "bearer-session-token",
role: input?.role ?? "owner",
client: {
...(input?.label ? { label: input.label } : {}),
deviceType: "bot",
},
...(input?.ttl ? { ttl: input.ttl } : {}),
})
.pipe(
Effect.flatMap((issued) => {
if (issued.method !== "bearer-session-token") {
return Effect.fail(
new AuthControlPlaneError({
message: "CLI session issuance produced an unexpected session method.",
}),
);
}
return Effect.succeed({
sessionId: issued.sessionId,
token: issued.token,
method: "bearer-session-token" as const,
role: issued.role,
subject: input?.subject ?? DEFAULT_SESSION_SUBJECT,
client: issued.client,
expiresAt: DateTime.toUtc(issued.expiresAt),
} satisfies IssuedBearerSession);
}),
Effect.mapError(toAuthControlPlaneError("Failed to issue session token.")),
);
const listSessions: AuthControlPlaneShape["listSessions"] = () =>
sessions.listActive().pipe(
Effect.map((activeSessions) => activeSessions.toSorted(bySessionPriority)),
Effect.mapError(toAuthControlPlaneError("Failed to list sessions.")),
);
const revokeSession: AuthControlPlaneShape["revokeSession"] = (sessionId) =>
sessions
.revoke(sessionId)
.pipe(Effect.mapError(toAuthControlPlaneError("Failed to revoke session.")));
const revokeOtherSessionsExcept: AuthControlPlaneShape["revokeOtherSessionsExcept"] = (
sessionId,
) =>
sessions
.revokeAllExcept(sessionId)
.pipe(Effect.mapError(toAuthControlPlaneError("Failed to revoke other sessions.")));
return {
createPairingLink,
listPairingLinks,
revokePairingLink,
issueSession,
listSessions,
revokeSession,
revokeOtherSessionsExcept,
} satisfies AuthControlPlaneShape;
});
export const AuthCoreLive = Layer.mergeAll(
BootstrapCredentialServiceLive,
SessionCredentialServiceLive,
);
export const AuthStorageLive = Layer.mergeAll(ServerSecretStoreLive, SqlitePersistenceLayerLive);
export const AuthRuntimeLive = AuthCoreLive.pipe(Layer.provideMerge(AuthStorageLive));
export const AuthControlPlaneLive = Layer.effect(AuthControlPlane, makeAuthControlPlane);
export const AuthControlPlaneRuntimeLive = AuthControlPlaneLive.pipe(
Layer.provideMerge(AuthRuntimeLive),
);