@dollhousemcp/mcp-server
Version:
DollhouseMCP - A Model Context Protocol (MCP) server that enables dynamic AI persona management from markdown files, allowing Claude and other compatible AI assistants to activate and switch between different behavioral personas.
178 lines • 6.96 kB
TypeScript
/**
* GitHub authentication manager using OAuth device flow
* Handles authentication for MCP servers without requiring client secrets
*/
import { TokenManager } from '../security/tokenManager.js';
import { APICache } from '../cache/APICache.js';
import { ConfigManager } from '../config/ConfigManager.js';
export interface DeviceCodeResponse {
device_code: string;
user_code: string;
verification_uri: string;
expires_in: number;
interval: number;
}
export interface TokenResponse {
access_token: string;
token_type: string;
scope: string;
}
export interface AuthStatus {
isAuthenticated: boolean;
hasToken: boolean;
username?: string;
scopes?: string[];
expiresAt?: Date;
}
/**
* Manages GitHub authentication using the OAuth device flow
* This is the recommended approach for CLI/desktop applications
*/
export declare class GitHubAuthManager {
/**
* DollhouseMCP's official OAuth App Client ID
* This is PUBLIC information - OAuth Client IDs are meant to be visible.
* Only Client Secrets are private (device flow doesn't use secrets).
*
* This Client ID enables the GitHub device flow authentication
* allowing users to authenticate with an 8-character code.
*/
private static readonly DEFAULT_CLIENT_ID;
private readonly DEVICE_CODE_URL;
private readonly TOKEN_URL;
private readonly USER_URL;
private readonly DEFAULT_POLL_INTERVAL;
private readonly MAX_POLL_ATTEMPTS;
private readonly configManager;
private apiCache;
private activePolling;
private tokenManager;
constructor(apiCache: APICache, configManager: ConfigManager, tokenManager: TokenManager);
/**
* Get the CLIENT_ID from environment variable, ConfigManager, or default
* Priority: Environment variable > ConfigManager > Default Client ID
*
* @returns The OAuth Client ID to use for authentication
*/
private getClientId;
/**
* Expose resolved client ID for external helpers while preserving internal logic.
*/
resolveClientId(): Promise<string | null>;
/**
* OAuth error codes that require immediate propagation per RFC 6749/8628.
* These are terminal errors that cannot be recovered by retrying.
*/
private static readonly TERMINAL_OAUTH_ERROR_CODES;
/**
* Error message patterns that indicate terminal OAuth errors.
* Used for message-based error detection when error codes aren't available.
*/
private static readonly TERMINAL_ERROR_PATTERNS;
/**
* Determines if an OAuth error is terminal and should propagate immediately.
*
* Per RFC 6749 (OAuth 2.0) and RFC 8628 (Device Authorization Grant):
* - Terminal errors (expired_token, access_denied) MUST stop polling immediately
* - Transient errors (network failures, slow_down) should be retried
*
* Error Detection Priority (most to least reliable):
* 1. Explicit error code parameter (from GitHub API response)
* 2. Error code embedded in Error object properties
* 3. Message pattern matching (fallback for compatibility)
*
* @param error - The error to check
* @param errorCode - Optional OAuth error code from API response
* @returns true if error is terminal and should propagate, false if retriable
*
* @see https://datatracker.ietf.org/doc/html/rfc8628#section-3.5
* @see https://docs.github.com/en/developers/apps/authorizing-oauth-apps
*/
private static isTerminalOAuthError;
/**
* Execute a network request with retry logic
*/
private fetchWithRetry;
/**
* Check current authentication status
*/
getAuthStatus(): Promise<AuthStatus>;
/**
* Initiate the device flow authentication process
*/
initiateDeviceFlow(): Promise<DeviceCodeResponse>;
/**
* Poll GitHub for OAuth token using device flow.
*
* Implements OAuth 2.0 Device Authorization Grant (RFC 8628) with proper error handling.
*
* ## OAuth 2.0 Compliance (RFC 6749/8628)
*
* ### Terminal Errors (MUST propagate immediately):
* - `expired_token` - Authorization code has expired, user must restart flow
* - `access_denied` - User explicitly denied authorization
* - `unsupported_grant_type` - Invalid grant type (configuration error)
* - `invalid_grant` - Invalid or expired device code
*
* ### Transient Errors (should be retried):
* - `authorization_pending` - User hasn't completed authorization yet
* - `slow_down` - Polling too frequently, increase interval
* - Network errors (ECONNREFUSED, ETIMEDOUT, etc.)
*
* ### Error Handling Flow:
* 1. GitHub returns error code in response (e.g., `{error: "expired_token"}`)
* 2. Check if error is terminal using `isTerminalOAuthError()`
* 3. Terminal errors throw immediately, stopping polling
* 4. Transient errors are logged and polling continues
* 5. After MAX_POLL_ATTEMPTS (15 minutes), timeout error is thrown
*
* @param deviceCode - Device code from GitHub authorization flow
* @param interval - Polling interval in milliseconds (default: 5000ms)
* @returns Promise resolving to TokenResponse with access token
* @throws {Error} Terminal OAuth errors (expired_token, access_denied, etc.)
* @throws {Error} Timeout after MAX_POLL_ATTEMPTS (180 attempts = 15 minutes)
* @throws {Error} Network errors that persist beyond retry logic
*
* @see https://datatracker.ietf.org/doc/html/rfc8628 - OAuth 2.0 Device Authorization Grant
* @see https://datatracker.ietf.org/doc/html/rfc6749 - OAuth 2.0 Authorization Framework
*/
pollForToken(deviceCode: string, interval?: number): Promise<TokenResponse>;
/**
* Complete the authentication flow and store the token
*/
completeAuthentication(tokenResponse: TokenResponse): Promise<AuthStatus>;
/**
* Clear stored authentication and revoke token
*/
clearAuthentication(): Promise<void>;
/**
* Store token securely using encrypted file storage
*/
private storeToken;
/**
* Fetch user information from GitHub
*/
private fetchUserInfo;
/**
* Format authentication instructions for users
*/
formatAuthInstructions(deviceResponse: DeviceCodeResponse): string;
/**
* Check if user needs to authenticate for a specific action
*/
needsAuthForAction(action: string): boolean;
/**
* Wait with abort signal support
*/
private waitWithAbort;
/**
* Clean up any active operations (called on server shutdown)
*/
cleanup(): Promise<void>;
/**
* Get user-friendly error message based on HTTP status code
* Avoids exposing sensitive information while providing helpful guidance
*/
private getErrorMessageForStatus;
}
//# sourceMappingURL=GitHubAuthManager.d.ts.map