/** * Bearer resolution for both transports. * * Two auth shapes are accepted on the HTTP transport: * * 1. `mt_live_…` API keys — direct, long-lived bearers minted by the * Meertrack app. Used by stdio (from `MEERTRACK_API_KEY` at startup), * custom-connector users who paste a key, and direct API consumers. * * 2. OAuth 2.1 access tokens (JWTs) — minted by the Meertrack authorization * server at `https://meertrack.com/oauth/token`. Used by Claude's * Connectors Directory and any other MCP client that does OAuth * discovery. Validated locally against the AS's JWKS (cached). * * Discrimination is by prefix: anything starting with `mt_live_` goes through * the API-key path (regex-validated, forwarded verbatim to upstream). Anything * else is treated as a JWT (signature + `iss` + `aud` + `exp` verified, then * forwarded verbatim to upstream, which MUST also accept JWTs). * * In both modes, the bearer is forwarded verbatim — the upstream API is the * single source of truth for authorization decisions. Local JWT verification * on the MCP server is required by MCP spec §Authorization so we can emit a * spec-conformant 401 with `WWW-Authenticate: resource_metadata=…` without a * round trip. */ export declare const API_KEY_PREFIX = "mt_live_"; /** * Scopes this resource server understands. Single source for both the PRM * `scopes_supported` array and the `scope` parameter of `WWW-Authenticate` * (RFC 6750 §3) — a client that has never fetched the PRM learns what to ask * for from the 401 alone, and the two lists cannot disagree. */ export declare const SUPPORTED_SCOPES: readonly ["read"]; /** * Why a request was rejected, as a stable machine-readable code. Emitted on the * 401 log line so auth failures are countable in aggregate. * * `jwt_bad_audience` is the one to alert on: it is the direct symptom of the * resource-identifier / `aud` drift that silently broke new authorizations for * a month in 2026-07. A 401 with no reason attached — which is all this server * used to log — makes that class of failure invisible. */ export type AuthOutcome = "no_credentials" | "api_key_malformed" | "jwt_expired" | "jwt_bad_audience" | "jwt_bad_issuer" | "jwt_bad_signature" | "jwt_malformed" | "jwt_missing_claims" | "jwt_unverified"; /** True iff `value` begins with `mt_live_`. Does not assert length/charset. */ export declare function hasApiKeyPrefix(value: string): boolean; /** Strict format check: prefix + non-empty base64url body. */ export declare function isWellFormedApiKey(value: string): boolean; export declare class MissingApiKeyError extends Error { constructor(message: string); } export declare class InvalidApiKeyError extends Error { constructor(message: string); } /** * stdio: resolve the bearer from the environment exactly once at startup. * Throws a descriptive error that the entrypoint can surface on stderr and * exit with. Never logs the key itself. */ export declare function resolveEnvApiKey(env?: NodeJS.ProcessEnv): string; /** * HTTP mode: resolution outcome per request. Successful cases carry the * forwardable bearer and which auth type was used. Failures carry everything * the transport needs to emit a spec-conformant 401 (WWW-Authenticate header * value included). */ export type HttpAuthResolution = { ok: true; bearer: string; authType: "api_key" | "oauth"; source: "header" | "query"; /** For OAuth: verified JWT claims. Undefined for api_key path. */ claims?: JwtClaims; } | { ok: false; status: 401; code: "unauthorized"; message: string; wwwAuthenticate: string; /** Machine-readable reason, for the 401 log line. Never sent to the client. */ authOutcome: AuthOutcome; }; /** Subset of JWT claims we care about after OAuth verification. */ export interface JwtClaims { sub: string; company_id: string; scope?: string; iss: string; aud: string; exp: number; iat: number; } export interface OAuthConfig { /** Expected `iss` claim. Must match exactly. */ issuer: string; /** Expected `aud` claim. RFC 8707 audience binding — this is the canonical MCP URI. */ audience: string; /** JWKS URL on the authorization server. Keys are fetched + cached by `jose`. */ jwksUrl: string; } export interface HttpAuthContext { /** Case-insensitive header lookup — works for `Headers`, plain objects, and Hono's helpers. */ header: (name: string) => string | null | undefined; /** Parsed search params for the inbound URL. */ searchParams: URLSearchParams; /** Public URL of the `/.well-known/oauth-protected-resource` document. */ protectedResourceMetadataUrl: string; /** * OAuth configuration. When undefined, only `mt_live_…` keys are accepted * (pre-OAuth deployments and tests that don't care about JWT paths). */ oauth?: OAuthConfig; } /** * Extract a bearer from an incoming HTTP request. Header wins over query when * both are set. If absent or malformed, return an `ok: false` resolution * carrying a fully-formed `WWW-Authenticate` header value — the transport * emits the 401 without touching the upstream API. */ export declare function extractHttpBearer(ctx: HttpAuthContext): Promise; /** Extract a bearer token from an `Authorization` header value, or `null` if absent/malformed. */ export declare function parseBearerHeader(value: string): string | null; /** * MCP spec §Authorization / RFC 9728: 401 responses on the HTTP transport * MUST advertise where the client can find Protected Resource Metadata. * Clients use this to discover the authorization server(s) and initiate the * OAuth 2.1 flow. * * Optional `reason` adds RFC 6750 §3 `error` + `error_description` params so * clients can tell "no token sent" (omit reason → prompt user) apart from * "token rejected" (`error="invalid_token"` → silently refresh). * * `scope` is always included — MCP (2025-11-25 §Authorization) says servers * SHOULD advertise it per RFC 6750 §3 so a client that has not yet fetched the * PRM still knows what to request at `/authorize`. It is sourced from * `SUPPORTED_SCOPES`, the same constant the PRM's `scopes_supported` uses. */ export declare function buildWwwAuthenticateHeader(protectedResourceMetadataUrl: string, reason?: { error: "invalid_token" | "insufficient_scope"; description: string; }): string; /** * Redact every `mt_live_…` token in `value`. Apply to any string before * writing it to logs or error messages. Also redacts `Bearer mt_live_…`. * JWTs are not redacted here — they're audience-bound and expire on their * own — but they are live bearer credentials for up to an hour, so never * log them. */ export declare function redactApiKeys(value: string): string; /** Test-only: clear the JWKS cache between tests. */ export declare function __resetJwksCache(): void; type OAuthVerification = { ok: true; claims: JwtClaims; } | { ok: false; message: string; outcome: AuthOutcome; }; /** * Verify an OAuth access token locally. Checks signature (via JWKS), `iss`, * `aud` (exact string match, RFC 8707 audience binding — this is what * prevents token passthrough between resources), and `exp`. Returns the * subset of claims the transport cares about, or a user-safe error message. */ export declare function verifyOAuthToken(token: string, config: OAuthConfig): Promise; export {}; //# sourceMappingURL=auth.d.ts.map