{"version":3,"file":"auth-middleware.cjs","names":["ORPCError"],"sources":["../../src/api/auth-middleware.ts"],"sourcesContent":["/**\n * Server-side auth middleware factory — framework home for the factory that\n * used to live as five near-identical copies (`api/src/lib/auth.ts` and each\n * plugin's `src/lib/auth.ts`). See citynode.app#207.\n *\n * Generic over the workspace's auth context (the generated `AuthPluginContext`\n * satisfies `AuthContextShape`), so each workspace imports the same factory and\n * passes its own context type for precise middleware narrowing:\n *\n *   const { requireAuth, requireAdmin, ... } = createAuthMiddleware<AuthContext>(builder);\n */\nimport type { DecoratedMiddleware } from \"@orpc/server\";\nimport { ORPCError } from \"@orpc/server\";\nimport type { z } from \"zod\";\n\n/** Structural minimum the middlewares read — the generated `AuthPluginContext` satisfies it. */\nexport interface AuthContextShape {\n  user?: { role?: string | null } | null;\n  userId?: string | null;\n  apiKey?: { permissions?: Record<string, string[]> | null } | null;\n  organization?: {\n    activeOrganizationId?: string | null;\n    member?: { id?: string | null; role?: string | null } | null;\n    organization?: { metadata?: Record<string, unknown> | null; status?: string } | null;\n  } | null;\n}\n\ntype OrgMetaType<TSchema extends z.ZodType | undefined> = TSchema extends z.ZodType\n  ? z.infer<TSchema>\n  : Record<string, unknown>;\n\nexport function createAuthMiddleware<\n  TContext extends AuthContextShape = AuthContextShape,\n  TOrgMetaSchema extends z.ZodType | undefined = undefined,\n>(builder: any, options?: { orgMetaSchema?: TOrgMetaSchema }) {\n  // TContext is used as-is (no intersection) so the narrowed middleware\n  // outputs stay exactly the workspace's own types when it passes its\n  // AuthPluginContext — the constraint only guarantees the guards can read\n  // the structural props.\n  type Ctx = TContext;\n  type TOrgMeta = OrgMetaType<TOrgMetaSchema>;\n  type UserMiddleware = DecoratedMiddleware<\n    Ctx,\n    { userId: string; user: NonNullable<Ctx[\"user\"]> },\n    any,\n    any,\n    any\n  >;\n  type OrgMiddleware = DecoratedMiddleware<\n    Ctx,\n    {\n      userId: string;\n      user: NonNullable<Ctx[\"user\"]>;\n      organization: NonNullable<Ctx[\"organization\"]> & {\n        activeOrganizationId: string;\n        organization:\n          | (NonNullable<NonNullable<Ctx[\"organization\"]>[\"organization\"]> & {\n              metadata: TOrgMeta | null;\n            })\n          | null;\n      };\n    },\n    any,\n    any,\n    any\n  >;\n  type MemberMiddleware = DecoratedMiddleware<\n    Ctx,\n    {\n      userId: string;\n      user: NonNullable<Ctx[\"user\"]>;\n      organization: NonNullable<Ctx[\"organization\"]> & {\n        activeOrganizationId: string;\n        member: NonNullable<NonNullable<Ctx[\"organization\"]>[\"member\"]>;\n        organization:\n          | (NonNullable<NonNullable<Ctx[\"organization\"]>[\"organization\"]> & {\n              metadata: TOrgMeta | null;\n            })\n          | null;\n      };\n    },\n    any,\n    any,\n    any\n  >;\n  type ApiKeyMiddleware = DecoratedMiddleware<\n    Ctx,\n    { apiKey: NonNullable<Ctx[\"apiKey\"]> },\n    any,\n    any,\n    any\n  >;\n\n  const requireAuth = builder.middleware(async ({ context, next }: { context: Ctx; next: any }) => {\n    if (!context.user || !context.userId) {\n      throw new ORPCError(\"UNAUTHORIZED\", {\n        message: \"Authentication required\",\n        data: { hint: \"Sign in to continue\" },\n      });\n    }\n    return next({ context: { userId: context.userId, user: context.user } });\n  }) as UserMiddleware;\n\n  const requireAuthOrApiKey = builder.middleware(\n    async ({ context, next }: { context: Ctx; next: any }) => {\n      if (!context.user && !context.userId && !context.apiKey) {\n        throw new ORPCError(\"UNAUTHORIZED\", {\n          message: \"Authentication required\",\n          data: { hint: \"Sign in or provide an API key\" },\n        });\n      }\n      return next({ context });\n    },\n  ) as DecoratedMiddleware<Ctx, Record<string, never>, any, any, any>;\n\n  const requireRole = <TRoles extends readonly string[]>(...roles: TRoles) =>\n    builder.middleware(async ({ context, next }: { context: Ctx; next: any }) => {\n      if (!context.user || !context.userId) {\n        throw new ORPCError(\"UNAUTHORIZED\", {\n          message: \"Authentication required\",\n          data: { authType: \"session\", hint: \"Sign in to continue\" },\n        });\n      }\n      const currentRole = context.user.role;\n      if (!currentRole || !roles.includes(currentRole)) {\n        throw new ORPCError(\"FORBIDDEN\", {\n          message: `Requires role: ${roles.join(\" or \")}`,\n          data: { requiredRoles: roles, currentRole },\n        });\n      }\n      return next({ context: { userId: context.userId, user: context.user } });\n    }) as UserMiddleware;\n\n  const requireAdmin = requireRole(\"admin\");\n\n  const requireOrganization = builder.middleware(\n    async ({ context, next }: { context: Ctx; next: any }) => {\n      if (!context.user || !context.userId) {\n        throw new ORPCError(\"UNAUTHORIZED\", {\n          message: \"Authentication required\",\n          data: { authType: \"session\", hint: \"Sign in to continue\" },\n        });\n      }\n      if (!context.organization?.activeOrganizationId) {\n        throw new ORPCError(\"FORBIDDEN\", {\n          message: \"Active organization required\",\n          data: { hint: \"Select or create an organization\" },\n        });\n      }\n      const org = context.organization;\n      assertOrganizationApproval(org.organization);\n      return next({\n        context: {\n          userId: context.userId,\n          user: context.user,\n          organization: {\n            ...org,\n            activeOrganizationId: org.activeOrganizationId,\n            organization: org.organization\n              ? {\n                  ...org.organization,\n                  metadata: parseOrgMetadata(org.organization.metadata, options?.orgMetaSchema),\n                }\n              : null,\n          },\n        },\n      });\n    },\n  ) as OrgMiddleware;\n\n  const requireOrgRole = <TRoles extends readonly string[]>(...roles: TRoles) =>\n    builder.middleware(async ({ context, next }: { context: Ctx; next: any }) => {\n      if (!context.user || !context.userId) {\n        throw new ORPCError(\"UNAUTHORIZED\", {\n          message: \"Authentication required\",\n          data: { authType: \"session\", hint: \"Sign in to continue\" },\n        });\n      }\n      if (!context.organization?.activeOrganizationId) {\n        throw new ORPCError(\"FORBIDDEN\", {\n          message: \"Active organization required\",\n          data: { hint: \"Select or create an organization\" },\n        });\n      }\n      const member = context.organization?.member;\n      if (!member?.id || !member?.role || !roles.includes(member.role)) {\n        throw new ORPCError(\"FORBIDDEN\", {\n          message: `Requires organization role: ${roles.join(\" or \")}`,\n          data: { requiredRoles: roles, currentRole: member?.role ?? null },\n        });\n      }\n      const org = context.organization;\n      assertOrganizationApproval(org.organization);\n      return next({\n        context: {\n          userId: context.userId,\n          user: context.user,\n          organization: {\n            ...org,\n            activeOrganizationId: org.activeOrganizationId,\n            member: { id: member.id, role: member.role },\n            organization: org.organization\n              ? {\n                  ...org.organization,\n                  metadata: parseOrgMetadata(org.organization.metadata, options?.orgMetaSchema),\n                }\n              : null,\n          },\n        },\n      });\n    }) as MemberMiddleware;\n\n  const requireApiKey = (requiredPermissions?: Record<string, string[]>) =>\n    builder.middleware(async ({ context, next }: { context: Ctx; next: any }) => {\n      if (!context.apiKey) {\n        throw new ORPCError(\"UNAUTHORIZED\", {\n          message: \"API key required\",\n          data: { authType: \"apiKey\", hint: \"Provide a valid API key via x-api-key header\" },\n        });\n      }\n      if (requiredPermissions) {\n        const keyPerms = context.apiKey.permissions ?? {};\n        for (const [resource, actions] of Object.entries(requiredPermissions)) {\n          const allowed = keyPerms[resource] ?? [];\n          const missing = actions.filter((a: string) => !allowed.includes(a));\n          if (missing.length > 0) {\n            throw new ORPCError(\"FORBIDDEN\", {\n              message: `API key lacks permission: ${resource}:${missing.join(\",\")}`,\n              data: { requiredPermissions, keyPermissions: keyPerms },\n            });\n          }\n        }\n      }\n      return next({ context: { apiKey: context.apiKey } });\n    }) as ApiKeyMiddleware;\n\n  return {\n    requireAuth,\n    requireAuthOrApiKey,\n    requireRole,\n    requireAdmin,\n    requireOrganization,\n    requireOrgRole,\n    requireApiKey,\n  };\n}\n\nfunction assertOrganizationApproval(\n  organization: NonNullable<AuthContextShape[\"organization\"]>[\"organization\"],\n) {\n  if (organization?.status && organization.status !== \"active\") {\n    throw new ORPCError(\"FORBIDDEN\", {\n      message: \"Organization requires platform-admin approval\",\n    });\n  }\n}\n\nfunction parseOrgMetadata<TSchema extends z.ZodType | undefined>(\n  raw: Record<string, unknown> | null | undefined,\n  schema: TSchema | undefined,\n): OrgMetaType<TSchema> | null {\n  if (!raw) return null;\n  if (!schema) return raw as OrgMetaType<TSchema>;\n  const result = schema.safeParse(raw);\n  if (result.success) return result.data as OrgMetaType<TSchema>;\n  throw new ORPCError(\"INTERNAL_SERVER_ERROR\", {\n    message: \"Invalid organization metadata\",\n    data: { errors: result.error.issues },\n  });\n}\n"],"mappings":"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"}