import { type ConnectionOwnership } from "@opengeni/contracts"; import type { Settings } from "@opengeni/config"; import { z } from "zod"; /** * Provider OAuth quirks as data. * * Some providers do not support DCR or CIMD and need a pre-registered client * with pinned metadata; some issue personal tokens only; some reject RFC 8707's * `resource` parameter. Each used to be a hand-written branch set inside * `oauth-client.ts`; every quirk is now a field on an `OAuthProviderProfile`. * The flow reads exactly one resolved profile and contains no provider-name * conditional. * * Profiles come from two layers: * * 1. Built-in profiles below, for the providers whose fences are security * invariants (hosted Slack MCP and official Gmail are personal-only, their * authorization servers are origin-pinned). These live in code as data so * the fences never depend on catalog import state. * 2. A validated `oauthProfile` object on a global catalog row (curated * overlay -> importer -> `capability_catalog_items.metadata`). A catalog * profile applies only when no built-in matches, and it can only narrow the * default behavior, never loosen a built-in fence. */ export declare const OFFICIAL_SLACK_MCP_URL: "https://mcp.slack.com/mcp"; export declare const OFFICIAL_GMAIL_MCP_URL = "https://gmailmcp.googleapis.com/mcp/v1"; export declare const OFFICIAL_GMAIL_MCP_SCOPES: readonly ["https://www.googleapis.com/auth/gmail.readonly", "https://www.googleapis.com/auth/gmail.compose", "https://www.googleapis.com/auth/gmail.modify"]; /** The slice of discovered authorization-server metadata the pins constrain. */ export type PinnableAuthorizationServer = { issuer: string; authorizationServer: string; authorizationEndpoint: string; tokenEndpoint: string; }; export type AuthorizationServerPins = { /** Origins `issuer` and `authorizationServer` must both belong to. */ issuerOrigins: readonly string[]; /** Origins the authorization endpoint must belong to. */ authorizationEndpointOrigins: readonly string[]; /** Origins the token endpoint must belong to. */ tokenEndpointOrigins: readonly string[]; /** Exact 422 message when a pin fails. */ message: string; /** Skip enforcement in a local test environment (loopback fixtures). */ skipInLocalTest: boolean; }; export type OAuthProviderProfile = { /** Stable identity for guards, logs, and tests. */ key: string; /** Built-in matching; catalog profiles match their own row's exact mcpUrl. */ match: { mcpUrls?: readonly string[]; providerDomains?: readonly string[]; }; /** * Canonical provider identity forced when the profile matches by URL, so a * caller cannot relabel a pinned resource under another domain. */ canonicalProviderDomain?: string; /** Reject a caller-supplied manual OAuth client (deployment-managed only). */ rejectCallerOAuthClient?: { message: string; }; /** The payload's explicit providerDomain must canonicalize to this domain. */ requireProviderDomain?: { domain: string; message: string; }; /** Outside a local test environment, the start URL must be exactly this. */ requireExactMcpUrl?: { url: string; message: string; }; /** Deployment-managed client credentials must be configured (503 otherwise). */ requireDeploymentClient?: { key: DeploymentManagedClientKey; message: string; }; /** Ownerships a connection may take; a singleton also sets the default. */ allowedOwnership: readonly ConnectionOwnership[]; /** Exact 422 message when an explicit disallowed ownership is requested. */ ownershipMessage?: string; /** Bind reconnect/dedupe to the exact mcpUrl, not just the provider domain. */ exactMcpBinding: boolean; /** How an existing connection is chosen for reconnect coalescing. */ connectionSelection: "canonical_personal" | "first_active"; /** Post-discovery authorization-server origin pins. */ authorizationServer?: AuthorizationServerPins; /** * Post-discovery identity check: the resolved providerDomain must equal this * domain (the reserved-authorization-server guard shares its message). */ postDiscoveryProviderDomain?: { domain: string; message: string; }; /** * Client registration preference. `dcr` or `cimd` forces that advertised * self-registration mechanism after operator/deployment clients. Without an * explicit preference, DCR wins when both mechanisms are advertised. */ clientSource?: "deployment_managed" | "cimd" | "dcr"; /** Send RFC 8707 `resource` on authorize and token requests. */ sendResourceParameter: boolean; /** Extra authorize-URL query parameters (e.g. offline-consent opts). */ extraAuthorizeParams?: Readonly>; /** Exact scope override; the caller can never widen past it. */ requestedScopes?: readonly string[]; }; export declare const DEFAULT_OAUTH_PROFILE: OAuthProviderProfile; /** Exact built-in profile by its stable key; throws on an unknown key. */ export declare function builtInOAuthProfileByKey(key: string): OAuthProviderProfile; export type DeploymentManagedClientKey = "slack"; /** * Pre-registered deployment clients resolved from dedicated settings, keyed by * the authorization-server origins they serve. Consulted before the operator * clients JSON so a deployment-managed provider can never be shadowed. */ export declare const DEPLOYMENT_MANAGED_CLIENTS: readonly { key: DeploymentManagedClientKey; issuerOrigins: readonly string[]; resolve: (settings: Settings) => { clientId: string; clientSecret: string; tokenEndpointAuthMethod: "client_secret_post"; } | null; }[]; export declare function deploymentManagedClientFor(settings: Settings, key: DeploymentManagedClientKey): ReturnType<(typeof DEPLOYMENT_MANAGED_CLIENTS)[number]["resolve"]>; /** Built-in profile for a start target, or null when only the default applies. */ export declare function builtInOAuthProfileFor(input: { mcpUrl: string; providerDomain?: string | undefined; }): OAuthProviderProfile | null; /** Ownership an omitted request defaults to under a profile. */ export declare function defaultOwnershipFor(profile: OAuthProviderProfile): ConnectionOwnership; /** Rejects an ownership outside the profile's allowed set with its exact message. */ export declare function assertOwnershipAllowed(profile: OAuthProviderProfile, ownership: ConnectionOwnership): void; /** Enforces a profile's authorization-server origin pins with its exact message. */ export declare function assertAuthorizationServerPins(as: PinnableAuthorizationServer, pins: Pick): void; /** * Reserved-authorization-server guard: a discovered identity claimed by one * profile is rejected on every other flow with that guard's exact message. */ export declare function assertAuthorizationServerNotReserved(as: PinnableAuthorizationServer, profile: OAuthProviderProfile): void; /** * Catalog-row profile: the declarative subset a curated row may carry. It can * only narrow the default flow; the fields that grant authority (deployment * client settings, reserved-server membership) are built-in-only by * construction because the schema cannot express them. */ /** * Authorize-URL parameters owned by the OAuth client itself. A profile's * `extraAuthorizeParams` may never name one: overriding `scope` or `resource` * would widen the grant past the recorded contract, and the rest carry the * PKCE/state security machinery. Enforced in this schema, in the curation * parser, and defensively again in `buildAuthorizationUrl`. */ export declare const RESERVED_AUTHORIZE_PARAMS: ReadonlySet; export declare const catalogOAuthProfileSchema: z.ZodObject<{ clientSource: z.ZodOptional>; exactMcpUrl: z.ZodOptional; pinnedIssuerOrigins: z.ZodOptional>; pinnedEndpointOrigins: z.ZodOptional>; sendResourceParameter: z.ZodOptional; allowedOwnership: z.ZodOptional>>; requestedScopes: z.ZodOptional>; extraAuthorizeParams: z.ZodOptional>; }, z.core.$strict>; export type CatalogOAuthProfile = z.infer; /** * Canonical catalog lookup key for an MCP URL, mirroring the importer's * `canonicalMcpUrl` (`scripts/catalog-curation.ts`): no fragment, lowercase * host, default ports stripped, trailing slashes collapsed. Without this, a * trailing-slash or uppercase-host variant of a profiled URL would miss the * row and silently fall back to the default profile. */ export declare function catalogMcpUrlKey(value: string): string; /** * Applies a validated catalog profile over the default profile for a row with * no built-in. `deployment_managed` from catalog data resolves through the * operator clients JSON (issuer-keyed) only; named settings-backed clients * remain built-in-only. * * A present-but-invalid profile fails closed with a 422: the row's operator * declared constraints, and silently degrading to the default profile would * drop an ownership fence or origin pin on a JSON typo. */ export declare function oauthProfileFromCatalog(mcpUrl: string, raw: unknown): OAuthProviderProfile;