@civic/auth-mcp
Version:
Civic Auth integration for MCP servers
157 lines (152 loc) • 6.19 kB
TypeScript
import { Request, RequestHandler } from 'express';
import { IncomingMessage } from 'node:http';
import { AuthInfo } from '@modelcontextprotocol/sdk/server/auth/types.js';
import { JWTPayload } from 'jose';
export { CLIAuthProvider, CLIAuthProviderOptions, CLIClient, CivicAuthProvider, CivicAuthProviderOptions, DEFAULT_CALLBACK_PORT, DEFAULT_MCP_ROUTE, DEFAULT_SCOPES, DEFAULT_WELLKNOWN_URL, InMemoryTokenPersistence, PUBLIC_CIVIC_CLIENT_ID, RestartableStreamableHTTPClientTransport, TokenAuthProvider, TokenAuthProviderOptions, TokenPersistence } from './client/index.js';
import '@modelcontextprotocol/sdk/client/auth.js';
import '@modelcontextprotocol/sdk/shared/auth.js';
import '@modelcontextprotocol/sdk/client/sse.js';
import '@modelcontextprotocol/sdk/client/streamableHttp.js';
import '@modelcontextprotocol/sdk/client/index.js';
interface CivicAuthOptions<TAuthInfo extends ExtendedAuthInfo, TRequest extends IncomingMessage = IncomingMessage> {
/**
* The URL to the auth server's well-known OIDC configuration
* Defaults to https://auth.civic.com/oauth/.well-known/openid-configuration
*/
wellKnownUrl?: string;
/**
* OAuth scopes to support
* Defaults to ['openid', 'profile', 'email']
*/
scopesSupported?: string[];
/**
* The issuer URL for the resource server
* Defaults to the server's base URL
*/
issuerUrl?: string | URL;
/**
* Base path for auth endpoints
* Defaults to '/'
*/
basePath?: string;
/**
* The MCP route to protect with authentication
* Defaults to '/mcp'
*/
mcpRoute?: string;
/**
* Optional callback to enrich the auth info with custom data
* Called after successful token verification
* @param authInfo The verified auth info from the token. Null if no token was provided.
* @param request Optional request object that may contain headers or other data
* @returns Enriched auth info with custom data
*/
onLogin?: (authInfo: ExtendedAuthInfo | null, request?: TRequest) => Promise<TAuthInfo | null>;
/**
* Optional OAuth client ID / Tenant ID.
* When set, the access token must include *either* a "client_id" field or "tid" field that matches it.
*/
clientId?: string;
/**
* Whether to allow dynamic client registration by adding client ID as subdomain.
* When true, the client ID will be added as a subdomain to the auth server URL.
* When false (default), the auth server URL will be used as-is without subdomain prefixing.
* Defaults to false.
*/
allowDynamicClientRegistration?: boolean;
}
interface OIDCWellKnownConfiguration {
issuer: string;
authorization_endpoint: string;
token_endpoint: string;
jwks_uri: string;
scopes_supported?: string[];
response_types_supported?: string[];
grant_types_supported?: string[];
token_endpoint_auth_methods_supported?: string[];
introspection_endpoint?: string;
revocation_endpoint?: string;
registration_endpoint?: string;
}
interface ExtendedAuthInfo extends AuthInfo {
extra?: {
sub?: string;
email?: string;
name?: string;
picture?: string;
};
}
/**
* Custom error class for all authentication errors
*/
declare class AuthenticationError extends Error {
}
/**
* Custom error class for JWT verification failures
*/
declare class JWTVerificationError extends AuthenticationError {
originalError?: Error | undefined;
constructor(message: string, originalError?: Error | undefined);
}
type AccessTokenPayload = JWTPayload & {
client_id: string | undefined;
tid: string | undefined;
};
/**
* Core authentication functionality that can be used with any framework
*/
declare class McpServerAuth<TAuthInfo extends ExtendedAuthInfo, TRequest extends IncomingMessage = IncomingMessage> {
private oidcConfig;
private jwks;
private options;
private constructor();
/**
* Initialize the auth core by fetching OIDC configuration
*/
static init<TAuthInfo extends ExtendedAuthInfo, TRequest extends IncomingMessage = IncomingMessage>(options?: CivicAuthOptions<TAuthInfo, TRequest>): Promise<McpServerAuth<TAuthInfo, TRequest>>;
/**
* Get the OAuth Protected Resource metadata
* @param issuerUrl The issuer URL of the resource server (e.g., https://my-server.com)
*/
getProtectedResourceMetadata(issuerUrl: string): {
resource: string;
authorization_servers: string[];
scopes_supported: string[];
bearer_methods_supported: string[];
};
/**
* Create auth info from a token (or null) and request
* @param token The JWT token (can be null)
* @param payload The JWT payload if token was already verified
* @param request Optional request object to pass to onLogin callback
* @returns ExtendedAuthInfo if successful, null otherwise
*/
private createAuthInfo;
/**
* Extract and verify bearer token from authorization header
* @param authHeader The Authorization header value
* @returns Object with token and payload if valid, throws if invalid token, returns null values if no token
*/
private extractBearerToken;
/**
* Handle a request by extracting and verifying the bearer token
* @param request The request object
* @returns ExtendedAuthInfo if valid
* @throws Error if authentication fails
*/
handleRequest(request: TRequest): Promise<TAuthInfo>;
}
/**
* Express middleware that configures an MCP server to use Civic Auth
* as its authorization server.
*
* This middleware:
* 1. Exposes /.well-known/oauth-protected-resource metadata
* 2. Validates bearer tokens using Civic's JWKS
* 3. Attaches user info to the request
*
* @param options Configuration options
* @returns Express middleware
*/
declare function auth<TAuthInfo extends ExtendedAuthInfo>(options?: CivicAuthOptions<TAuthInfo, Request>): Promise<RequestHandler>;
export { type AccessTokenPayload, AuthenticationError, type CivicAuthOptions, type ExtendedAuthInfo, JWTVerificationError, McpServerAuth, type OIDCWellKnownConfiguration, auth };