86 lines · 4.1 KB · TypeScript
RawDownload
import { Effect, Layer } from "effect"; import { IdentityProvider, Unauthorized } from "@executor-js/api/server"; import { BetterAuth } from "./better-auth"; // ---------------------------------------------------------------------------// The self-host identity seam — the production implementation of the shared// `IdentityProvider` from `@executor-js/api/server`, which resolves an incoming// request to a Principal. WorkOS (cloud) and Better Auth (self-host) are// interchangeable implementations of the same tag; nothing downstream knows// which is wired.////   - succeeds with a Principal      -> authenticated//   - fails Unauthorized             -> no/invalid credential (renders 401)//   - fails NoOrganization           -> valid credential, no org (renders 403)//// `betterAuthIdentityLayer` is the only production provider. The trivial fake// identities tests inject live in `src/testing/test-app.ts`.// --------------------------------------------------------------------------- const bearerToken = (headers: Headers): string | undefined => {  const authorization = headers.get("authorization");  if (!authorization) return undefined;  return authorization.toLowerCase().startsWith("bearer ")    ? authorization.slice(7).trim() || undefined    : undefined;}; // ---------------------------------------------------------------------------// The production IdentityProvider: resolve a request to a Better Auth session// and map it to a neutral Principal. Three credential shapes resolve here://   - session cookie (browser SPA)//   - Bearer session token (bearer plugin)//   - Bearer API key — the apiKey plugin reads `x-api-key`, so when the normal//     resolution fails we retry with the Bearer value as x-api-key, which (with//     enableSessionForAPIKeys) mints the owner's session. This is what lets a//     generated API key authenticate the API + MCP endpoint as a Bearer token.// Single-org instance, so organizationName is the boot-cached org name.// --------------------------------------------------------------------------- export const betterAuthIdentityLayer: Layer.Layer<IdentityProvider, never, BetterAuth> =  Layer.effect(IdentityProvider)(    Effect.gen(function* () {      const { auth, organizationId, organizationName, organizationSlug } = yield* BetterAuth;      return IdentityProvider.of({        authenticate: (request) =>          Effect.gen(function* () {            let resolved = yield* Effect.promise(() =>              auth.api.getSession({ headers: request.headers }),            );            if (!resolved) {              const token = bearerToken(request.headers);              if (token) {                resolved = yield* Effect.tryPromise({                  try: () => auth.api.getSession({ headers: { "x-api-key": token } }),                  catch: () => "api-key session lookup failed",                }).pipe(Effect.orElseSucceed(() => null));              }            }            // No session resolved from any credential shape -> unauthenticated.            // The middleware's failure strategy renders this as a 401.            if (!resolved) return yield* new Unauthorized();            // Single-org instance: every authenticated user belongs to the one            // seeded org. Cookie/bearer-session logins are pinned to it by the            // session hook; API-key-minted sessions carry no active org, so we            // default to the seeded org rather than rejecting with NoOrganization.            const resolvedOrganizationId = resolved.session.activeOrganizationId ?? organizationId;            return {              kind: "member" as const,              accountId: resolved.user.id,              organizationId: resolvedOrganizationId,              organizationName,              organizationSlug,              email: resolved.user.email,              name: resolved.user.name ?? null,              avatarUrl: resolved.user.image ?? null,              roles: (resolved.user.role ?? "user")                .split(",")                .map((role) => role.trim())                .filter((role) => role.length > 0),            };          }),      });    }),  );