@mcp-z/oauth-microsoft 1.0.0 ā 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/dist/cjs/index.d.cts +2 -1
- package/dist/cjs/index.d.ts +2 -1
- package/dist/cjs/index.js +4 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/lib/dcr-router.js.map +1 -1
- package/dist/cjs/lib/dcr-utils.js.map +1 -1
- package/dist/cjs/lib/dcr-verify.js.map +1 -1
- package/dist/cjs/lib/fetch-with-timeout.js.map +1 -1
- package/dist/cjs/lib/loopback-router.d.cts +8 -0
- package/dist/cjs/lib/loopback-router.d.ts +8 -0
- package/dist/cjs/lib/loopback-router.js +219 -0
- package/dist/cjs/lib/loopback-router.js.map +1 -0
- package/dist/cjs/lib/token-verifier.js.map +1 -1
- package/dist/cjs/providers/dcr.js.map +1 -1
- package/dist/cjs/providers/device-code.js.map +1 -1
- package/dist/cjs/providers/loopback-oauth.d.cts +15 -17
- package/dist/cjs/providers/loopback-oauth.d.ts +15 -17
- package/dist/cjs/providers/loopback-oauth.js +190 -156
- package/dist/cjs/providers/loopback-oauth.js.map +1 -1
- package/dist/cjs/schemas/index.js.map +1 -1
- package/dist/cjs/setup/config.d.cts +4 -1
- package/dist/cjs/setup/config.d.ts +4 -1
- package/dist/cjs/setup/config.js +3 -0
- package/dist/cjs/setup/config.js.map +1 -1
- package/dist/cjs/types.js.map +1 -1
- package/dist/esm/index.d.ts +2 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lib/dcr-router.js.map +1 -1
- package/dist/esm/lib/dcr-utils.js.map +1 -1
- package/dist/esm/lib/dcr-verify.js.map +1 -1
- package/dist/esm/lib/fetch-with-timeout.js.map +1 -1
- package/dist/esm/lib/loopback-router.d.ts +8 -0
- package/dist/esm/lib/loopback-router.js +32 -0
- package/dist/esm/lib/loopback-router.js.map +1 -0
- package/dist/esm/lib/token-verifier.js.map +1 -1
- package/dist/esm/providers/dcr.js.map +1 -1
- package/dist/esm/providers/device-code.js +2 -2
- package/dist/esm/providers/device-code.js.map +1 -1
- package/dist/esm/providers/loopback-oauth.d.ts +15 -17
- package/dist/esm/providers/loopback-oauth.js +133 -115
- package/dist/esm/providers/loopback-oauth.js.map +1 -1
- package/dist/esm/schemas/index.js.map +1 -1
- package/dist/esm/setup/config.d.ts +4 -1
- package/dist/esm/setup/config.js +3 -0
- package/dist/esm/setup/config.js.map +1 -1
- package/dist/esm/types.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/oauth/oauth-microsoft/src/providers/loopback-oauth.ts"],"sourcesContent":["/**\n * Loopback OAuth Implementation for Microsoft (RFC 8252)\n *\n * Implements OAuth 2.0 Authorization Code Flow with PKCE using loopback interface redirection.\n * Uses ephemeral local server with OS-assigned port (RFC 8252 Section 8.3).\n * Eliminates port conflicts by using port 0, allowing unlimited concurrent servers.\n *\n * Flow:\n * 1. Check token cache (memory + storage)\n * 2. If cache miss: Start ephemeral server on port 0\n * 3. Generate auth URL with dynamic redirect (localhost:{assigned-port})\n * 4. Open browser or print URL (headless flag controls behavior)\n * 5. Handle callback, exchange code for token\n * 6. Cache token to storage\n * 7. Close ephemeral server\n */\n\nimport { addAccount, generatePKCE, getActiveAccount, getErrorTemplate, getSuccessTemplate, getToken, listAccountIds, type OAuth2TokenStorageProvider, setAccountInfo, setActiveAccount, setToken } from '@mcp-z/oauth';\nimport * as http from 'http';\nimport open from 'open';\nimport { fetchWithTimeout } from '../lib/fetch-with-timeout.ts';\nimport { type AuthContext, type AuthFlowDescriptor, AuthRequiredError, type CachedToken, type EnrichedExtra, type LoopbackOAuthConfig } from '../types.ts';\n\ninterface TokenResponse {\n access_token: string;\n refresh_token?: string;\n expires_in?: number;\n scope?: string;\n token_type?: string;\n}\n\n/**\n * Loopback OAuth Client (RFC 8252 Section 7.3)\n *\n * Implements OAuth 2.0 Authorization Code Flow with PKCE for native applications\n * using loopback interface redirection. Manages ephemeral OAuth flows and token persistence\n * with Keyv for key-based token storage using compound keys.\n *\n * Token key format: {accountId}:{service}:token (e.g., \"user@example.com:outlook:token\")\n */\nexport class LoopbackOAuthProvider implements OAuth2TokenStorageProvider {\n private config: LoopbackOAuthConfig;\n\n constructor(config: LoopbackOAuthConfig) {\n this.config = config;\n }\n\n /**\n * Get access token from Keyv using compound key\n *\n * @param accountId - Account identifier (email address). Required for loopback OAuth.\n * @returns Access token for API requests\n */\n async getAccessToken(accountId?: string): Promise<string> {\n const { logger, service, tokenStore } = this.config;\n\n // Use active account if no accountId specified\n const effectiveAccountId = accountId ?? (await getActiveAccount(tokenStore, { service }));\n\n // If we have an accountId, try to use existing token\n if (effectiveAccountId) {\n logger.debug('Getting access token', { service, accountId: effectiveAccountId });\n\n // Check Keyv for token using new key format\n const storedToken = await getToken<CachedToken>(tokenStore, { accountId: effectiveAccountId, service });\n\n if (storedToken && this.isTokenValid(storedToken)) {\n logger.debug('Using stored access token', { accountId: effectiveAccountId });\n return storedToken.accessToken;\n }\n\n // If stored token expired but has refresh token, try refresh\n if (storedToken?.refreshToken) {\n try {\n logger.info('Refreshing expired access token', { accountId: effectiveAccountId });\n const refreshedToken = await this.refreshAccessToken(storedToken.refreshToken);\n await setToken(tokenStore, { accountId: effectiveAccountId, service }, refreshedToken);\n return refreshedToken.accessToken;\n } catch (error) {\n logger.info('Token refresh failed, starting new OAuth flow', {\n accountId: effectiveAccountId,\n error: error instanceof Error ? error.message : String(error),\n });\n // Fall through to new OAuth flow\n }\n }\n }\n\n // No valid token or no account - check if we can start OAuth flow\n const { headless } = this.config;\n if (headless) {\n // In headless mode (production), cannot start OAuth flow\n // Throw AuthRequiredError with auth_url descriptor for MCP tool response\n const { clientId, tenantId, scope } = this.config;\n\n // Incremental OAuth detection: Check if other accounts exist\n const existingAccounts = await this.getExistingAccounts();\n const hasOtherAccounts = effectiveAccountId ? existingAccounts.length > 0 && !existingAccounts.includes(effectiveAccountId) : existingAccounts.length > 0;\n\n // Build informational OAuth URL for headless mode\n // Note: No redirect_uri included - user must use account-add tool which starts proper ephemeral server\n const authUrl = new URL(`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize`);\n authUrl.searchParams.set('client_id', clientId);\n authUrl.searchParams.set('response_type', 'code');\n authUrl.searchParams.set('scope', scope);\n authUrl.searchParams.set('response_mode', 'query');\n authUrl.searchParams.set('prompt', 'select_account');\n\n // Provide context-aware hint based on existing accounts\n let hint: string;\n if (hasOtherAccounts) {\n hint = `Existing ${service} accounts found. Use account-list to view, account-switch to change account, or account-add to add new account`;\n } else if (effectiveAccountId) {\n hint = `Use account-add to authenticate ${effectiveAccountId}`;\n } else {\n hint = 'Use account-add to authenticate interactively';\n }\n\n const baseDescriptor = {\n kind: 'auth_url' as const,\n provider: 'microsoft',\n url: authUrl.toString(),\n hint,\n };\n\n const descriptor: AuthFlowDescriptor & { accountId?: string } = effectiveAccountId ? { ...baseDescriptor, accountId: effectiveAccountId } : baseDescriptor;\n\n throw new AuthRequiredError(descriptor);\n }\n\n // Interactive mode - start ephemeral OAuth flow\n logger.info('Starting ephemeral OAuth flow', { service, headless });\n const { token, email } = await this.performEphemeralOAuthFlow();\n\n // Store token with email as accountId\n await setToken(tokenStore, { accountId: email, service }, token);\n\n // Register account in account management system\n await addAccount(tokenStore, { service, accountId: email });\n\n // Set as active account so subsequent getAccessToken() calls find it\n await setActiveAccount(tokenStore, { service, accountId: email });\n\n // Store account metadata (email, added timestamp)\n await setAccountInfo(\n tokenStore,\n { service, accountId: email },\n {\n email,\n addedAt: new Date().toISOString(),\n }\n );\n\n logger.info('OAuth flow completed', { service, accountId: email });\n\n return token.accessToken;\n }\n\n /**\n * Convert to Microsoft Graph-compatible auth provider\n *\n * @param accountId - Account identifier for multi-account support (e.g., 'user@example.com')\n * @returns Auth provider configured for the specified account\n */\n toAuthProvider(accountId?: string): { getAccessToken: () => Promise<string> } {\n // Capture accountId in closure to pass to getAccessToken\n const getToken = () => this.getAccessToken(accountId);\n\n return {\n getAccessToken: getToken,\n };\n }\n\n /**\n * Authenticate new account with OAuth flow\n * Triggers account selection, stores token, registers account\n *\n * @returns Email address of newly authenticated account\n * @throws Error in headless mode (cannot open browser for OAuth)\n */\n async authenticateNewAccount(): Promise<string> {\n const { logger, headless, service, tokenStore } = this.config;\n\n if (headless) {\n throw new Error('Cannot authenticate new account in headless mode - interactive OAuth required');\n }\n\n logger.info('Starting new account authentication', { service });\n\n // Trigger OAuth with account selection\n const { token, email } = await this.performEphemeralOAuthFlow();\n\n // Store token\n await setToken(tokenStore, { accountId: email, service }, token);\n\n // Register account\n await addAccount(tokenStore, { service, accountId: email });\n\n // Set as active account\n await setActiveAccount(tokenStore, { service, accountId: email });\n\n // Store account metadata\n await setAccountInfo(\n tokenStore,\n { service, accountId: email },\n {\n email,\n addedAt: new Date().toISOString(),\n }\n );\n\n logger.info('New account authenticated', { service, email });\n return email;\n }\n\n /**\n * Get user email from Microsoft Graph API (pure query)\n * Used to query email for existing authenticated account\n *\n * @param accountId - Account identifier to get email for\n * @returns User's email address\n */\n async getUserEmail(accountId?: string): Promise<string> {\n // Get token for existing account\n const token = await this.getAccessToken(accountId);\n\n // Fetch email from Microsoft Graph\n const response = await fetchWithTimeout('https://graph.microsoft.com/v1.0/me', {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!response.ok) {\n throw new Error(`Failed to get user info: ${response.status} ${await response.text()}`);\n }\n\n const userInfo = (await response.json()) as { mail?: string; userPrincipalName: string };\n return userInfo.mail ?? userInfo.userPrincipalName;\n }\n\n /**\n * Check for existing accounts in token storage (incremental OAuth detection)\n *\n * Uses key-utils helper for forward compatibility with key format changes.\n *\n * @returns Array of account IDs that have tokens for this service\n */\n private async getExistingAccounts(): Promise<string[]> {\n const { service, tokenStore } = this.config;\n return listAccountIds(tokenStore, service);\n }\n\n private isTokenValid(token: CachedToken): boolean {\n if (!token.expiresAt) return true; // No expiry = assume valid\n return Date.now() < token.expiresAt - 60000; // 1 minute buffer\n }\n\n /**\n * Fetch user email from Microsoft Graph using access token\n * Called during OAuth flow to get email for accountId\n *\n * @param accessToken - Fresh access token from OAuth exchange\n * @returns User's email address (mail field or userPrincipalName fallback)\n */\n private async fetchUserEmailFromToken(accessToken: string): Promise<string> {\n const { logger } = this.config;\n\n const response = await fetchWithTimeout('https://graph.microsoft.com/v1.0/me', {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to fetch user email: HTTP ${response.status} - ${errorText}`);\n }\n\n const userInfo = (await response.json()) as { mail?: string; userPrincipalName: string };\n const email = userInfo.mail ?? userInfo.userPrincipalName;\n\n logger.debug('Fetched user email from Graph API', { email });\n return email;\n }\n\n private async performEphemeralOAuthFlow(): Promise<{ token: CachedToken; email: string }> {\n const { clientId, tenantId, scope, headless, logger, redirectUri: configRedirectUri } = this.config;\n\n // Parse redirectUri if provided to extract host, protocol, port, and path\n let targetHost = 'localhost'; // Default: localhost (Microsoft requires exact match with registered redirect URI)\n let targetPort = 0; // Default: OS-assigned ephemeral port\n let targetProtocol = 'http:'; // Default: http\n let callbackPath = '/callback'; // Default callback path\n let useConfiguredUri = false;\n\n if (configRedirectUri) {\n try {\n const parsed = new URL(configRedirectUri);\n\n // Use configured redirect URI as-is for production deployments\n targetHost = parsed.hostname;\n targetProtocol = parsed.protocol;\n\n // Extract port from URL (use default ports if not specified)\n if (parsed.port) {\n targetPort = Number.parseInt(parsed.port, 10);\n } else {\n targetPort = parsed.protocol === 'https:' ? 443 : 80;\n }\n\n // Extract path (default to /callback if URL has no path or just '/')\n if (parsed.pathname && parsed.pathname !== '/') {\n callbackPath = parsed.pathname;\n }\n\n useConfiguredUri = true;\n\n logger.debug('Using configured redirect URI', {\n host: targetHost,\n protocol: targetProtocol,\n port: targetPort,\n path: callbackPath,\n redirectUri: configRedirectUri,\n });\n } catch (error) {\n logger.warn('Failed to parse redirectUri, using ephemeral defaults', {\n redirectUri: configRedirectUri,\n error: error instanceof Error ? error.message : String(error),\n });\n // Continue with defaults (127.0.0.1, port 0, http, /callback)\n }\n }\n\n return new Promise((resolve, reject) => {\n // Generate PKCE challenge\n const { verifier: codeVerifier, challenge: codeChallenge } = generatePKCE();\n\n let server: http.Server | null = null;\n let serverPort: number;\n let finalRedirectUri: string; // Will be set in server.listen callback\n\n // Create ephemeral server with OS-assigned port (RFC 8252)\n server = http.createServer(async (req, res) => {\n if (!req.url) {\n res.writeHead(400, { 'Content-Type': 'text/html' });\n res.end(getErrorTemplate('Invalid request'));\n server?.close();\n reject(new Error('Invalid request: missing URL'));\n return;\n }\n const url = new URL(req.url, `http://localhost:${serverPort}`);\n\n if (url.pathname === callbackPath) {\n const code = url.searchParams.get('code');\n const error = url.searchParams.get('error');\n\n if (error) {\n res.writeHead(400, { 'Content-Type': 'text/html' });\n res.end(getErrorTemplate(error));\n server?.close();\n reject(new Error(`OAuth error: ${error}`));\n return;\n }\n\n if (!code) {\n res.writeHead(400, { 'Content-Type': 'text/html' });\n res.end(getErrorTemplate('No authorization code received'));\n server?.close();\n reject(new Error('No authorization code received'));\n return;\n }\n\n try {\n // Exchange code for token (must use same redirect_uri as in authorization request)\n const tokenResponse = await this.exchangeCodeForToken(code, codeVerifier, finalRedirectUri);\n\n // Build cached token\n const cachedToken: CachedToken = {\n accessToken: tokenResponse.access_token,\n ...(tokenResponse.refresh_token !== undefined && { refreshToken: tokenResponse.refresh_token }),\n ...(tokenResponse.expires_in !== undefined && { expiresAt: Date.now() + tokenResponse.expires_in * 1000 }),\n ...(tokenResponse.scope !== undefined && { scope: tokenResponse.scope }),\n };\n\n // Fetch user email immediately using the new access token\n const email = await this.fetchUserEmailFromToken(tokenResponse.access_token);\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getSuccessTemplate());\n server?.close();\n resolve({ token: cachedToken, email });\n } catch (exchangeError) {\n logger.error('Token exchange failed', { error: exchangeError instanceof Error ? exchangeError.message : String(exchangeError) });\n res.writeHead(500, { 'Content-Type': 'text/html' });\n res.end(getErrorTemplate('Token exchange failed'));\n server?.close();\n reject(exchangeError);\n }\n } else {\n res.writeHead(404, { 'Content-Type': 'text/plain' });\n res.end('Not Found');\n }\n });\n\n // Listen on targetPort (0 for OS assignment, or custom port from redirectUri)\n server.listen(targetPort, targetHost, () => {\n const address = server?.address();\n if (!address || typeof address === 'string') {\n server?.close();\n reject(new Error('Failed to start ephemeral server'));\n return;\n }\n\n serverPort = address.port;\n\n // Construct final redirect URI\n if (useConfiguredUri && configRedirectUri) {\n // Use configured redirect URI as-is for production\n finalRedirectUri = configRedirectUri;\n } else {\n // Construct ephemeral redirect URI with actual server port\n finalRedirectUri = `${targetProtocol}//${targetHost}:${serverPort}${callbackPath}`;\n }\n\n // Build Microsoft auth URL\n const authUrl = new URL(`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize`);\n authUrl.searchParams.set('client_id', clientId);\n authUrl.searchParams.set('redirect_uri', finalRedirectUri);\n authUrl.searchParams.set('response_type', 'code');\n authUrl.searchParams.set('scope', scope);\n authUrl.searchParams.set('response_mode', 'query');\n authUrl.searchParams.set('code_challenge', codeChallenge);\n authUrl.searchParams.set('code_challenge_method', 'S256');\n authUrl.searchParams.set('prompt', 'select_account');\n\n logger.info('Ephemeral OAuth server started', { port: serverPort, headless });\n\n if (headless) {\n // Headless mode: Print auth URL to stderr (stdout is MCP protocol)\n console.error('\\nš OAuth Authorization Required');\n console.error('š Please visit this URL in your browser:\\n');\n console.error(` ${authUrl.toString()}\\n`);\n console.error('ā³ Waiting for authorization...\\n');\n } else {\n // Interactive mode: Open browser automatically\n logger.info('Opening browser for OAuth authorization');\n open(authUrl.toString()).catch((error) => {\n logger.info('Failed to open browser automatically', { error: error.message });\n console.error('\\nš OAuth Authorization Required');\n console.error(` ${authUrl.toString()}\\n`);\n });\n }\n });\n\n // Timeout after 5 minutes\n setTimeout(\n () => {\n if (server) {\n server.close();\n reject(new Error('OAuth flow timed out after 5 minutes'));\n }\n },\n 5 * 60 * 1000\n );\n });\n }\n\n private async exchangeCodeForToken(code: string, codeVerifier: string, redirectUri: string): Promise<TokenResponse> {\n const { clientId, clientSecret, tenantId } = this.config;\n\n const tokenUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;\n const params: Record<string, string> = {\n code,\n client_id: clientId,\n redirect_uri: redirectUri,\n grant_type: 'authorization_code',\n code_verifier: codeVerifier,\n };\n\n // Only include client_secret for confidential clients\n if (clientSecret) {\n params.client_secret = clientSecret;\n }\n\n const body = new URLSearchParams(params);\n\n const response = await fetchWithTimeout(tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: body.toString(),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token exchange failed: ${response.status} ${errorText}`);\n }\n\n return (await response.json()) as TokenResponse;\n }\n\n private async refreshAccessToken(refreshToken: string): Promise<CachedToken> {\n const { clientId, clientSecret, tenantId, scope } = this.config;\n\n const tokenUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;\n const params: Record<string, string> = {\n refresh_token: refreshToken,\n client_id: clientId,\n grant_type: 'refresh_token',\n scope,\n };\n\n // Only include client_secret for confidential clients\n if (clientSecret) {\n params.client_secret = clientSecret;\n }\n\n const body = new URLSearchParams(params);\n\n const response = await fetchWithTimeout(tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: body.toString(),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token refresh failed: ${response.status} ${errorText}`);\n }\n\n const tokenResponse = (await response.json()) as TokenResponse;\n\n return {\n accessToken: tokenResponse.access_token,\n refreshToken: refreshToken, // Keep original refresh token\n ...(tokenResponse.expires_in !== undefined && { expiresAt: Date.now() + tokenResponse.expires_in * 1000 }),\n ...(tokenResponse.scope !== undefined && { scope: tokenResponse.scope }),\n };\n }\n\n /**\n * Create auth middleware for single-user context (single active account per service)\n *\n * Single-user mode:\n * - Maintains per-service active accounts in storage\n * - Supports backchannel account override via extra._meta.accountId\n * - Automatically enhances output schemas with auth_required branch\n *\n * Example:\n * ```typescript\n * const loopback = new LoopbackOAuthProvider({ service: 'outlook', ... });\n * const middleware = loopback.authMiddleware();\n * const tools = toolFactories.map(f => f()).map(middleware.withToolAuth);\n * const resources = resourceFactories.map(f => f()).map(middleware.withResourceAuth);\n * const prompts = promptFactories.map(f => f()).map(middleware.withPromptAuth);\n * ```\n *\n * @returns Object with withToolAuth, withResourceAuth, withPromptAuth methods\n */\n authMiddleware() {\n const { service, tokenStore, logger } = this.config;\n\n // Shared wrapper logic - extracts extra parameter from specified position\n // Generic T captures the actual module type; handler is cast from unknown to callable\n const wrapAtPosition = <T extends { name: string; handler: unknown; [key: string]: unknown }>(module: T, extraPosition: number): T => {\n const operation = module.name;\n const originalHandler = module.handler as (...args: unknown[]) => Promise<unknown>;\n\n const wrappedHandler = async (...allArgs: unknown[]) => {\n // Extract extra from the correct position (defensive: handle arg-less tool pattern)\n let extra: EnrichedExtra;\n if (allArgs.length <= extraPosition) {\n // Arg-less tool pattern: keep args as-is, create separate extra object\n extra = (allArgs[0] && typeof allArgs[0] === 'object' ? {} : {}) as EnrichedExtra;\n allArgs[extraPosition] = extra;\n } else {\n extra = (allArgs[extraPosition] || {}) as EnrichedExtra;\n allArgs[extraPosition] = extra;\n }\n\n try {\n // Check for backchannel override via _meta.accountId\n let accountId: string | undefined;\n try {\n accountId = extra._meta?.accountId ?? (await getActiveAccount(tokenStore, { service }));\n } catch (error) {\n if (error instanceof Error && ((error as { code?: string }).code === 'REQUIRES_AUTHENTICATION' || error.name === 'AccountManagerError')) {\n accountId = undefined;\n } else {\n throw error;\n }\n }\n\n // Eagerly validate token exists or trigger OAuth flow\n await this.getAccessToken(accountId);\n\n // After OAuth flow completes, get the actual accountId (email) that was set\n const effectiveAccountId = accountId ?? (await getActiveAccount(tokenStore, { service }));\n if (!effectiveAccountId) {\n throw new Error(`No account found after OAuth flow for service ${service}`);\n }\n\n const auth = this.toAuthProvider(effectiveAccountId);\n\n // Inject authContext and logger into extra\n (extra as { authContext?: AuthContext }).authContext = {\n auth,\n accountId: effectiveAccountId,\n };\n (extra as { logger?: unknown }).logger = logger;\n\n // Call original handler with all args\n return await originalHandler(...allArgs);\n } catch (error) {\n // Token retrieval/refresh failed - return auth required\n if (error instanceof AuthRequiredError) {\n logger.info('Authentication required', {\n service,\n tool: operation,\n descriptor: error.descriptor,\n });\n\n // Return auth_required response wrapped in { result } to match tool outputSchema pattern\n // Tools define outputSchema: z.object({ result: discriminatedUnion(...) }) where auth_required is a branch\n const authRequiredResponse = {\n type: 'auth_required' as const,\n provider: service,\n message: `Authentication required for ${operation}. Please authenticate with ${service}.`,\n url: error.descriptor.kind === 'auth_url' ? error.descriptor.url : undefined,\n };\n\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify({ result: authRequiredResponse }),\n },\n ],\n structuredContent: { result: authRequiredResponse },\n };\n }\n\n // Other errors - propagate\n throw error;\n }\n };\n\n return {\n ...module,\n handler: wrappedHandler,\n } as T;\n };\n\n return {\n // Use structural constraints to avoid contravariance check on handler type.\n // wrapAtPosition is now generic and returns T directly.\n withToolAuth: <T extends { name: string; config: unknown; handler: unknown }>(module: T) => wrapAtPosition(module, 1),\n withResourceAuth: <T extends { name: string; template?: unknown; config?: unknown; handler: unknown }>(module: T) => wrapAtPosition(module, 2),\n withPromptAuth: <T extends { name: string; config: unknown; handler: unknown }>(module: T) => wrapAtPosition(module, 0),\n };\n }\n}\n\n/**\n * Create a loopback OAuth client for Microsoft services\n * Works for both stdio and HTTP transports\n */\nexport function createMicrosoftFileAuth(config: LoopbackOAuthConfig): OAuth2TokenStorageProvider {\n return new LoopbackOAuthProvider(config);\n}\n"],"names":["LoopbackOAuthProvider","createMicrosoftFileAuth","config","getAccessToken","accountId","logger","service","tokenStore","effectiveAccountId","storedToken","refreshedToken","error","headless","clientId","tenantId","scope","existingAccounts","hasOtherAccounts","authUrl","hint","baseDescriptor","descriptor","token","email","getActiveAccount","debug","getToken","isTokenValid","accessToken","refreshToken","info","refreshAccessToken","setToken","Error","message","String","getExistingAccounts","length","includes","URL","searchParams","set","kind","provider","url","toString","AuthRequiredError","performEphemeralOAuthFlow","addAccount","setActiveAccount","setAccountInfo","addedAt","Date","toISOString","toAuthProvider","authenticateNewAccount","getUserEmail","userInfo","response","fetchWithTimeout","headers","Authorization","ok","status","text","json","mail","userPrincipalName","listAccountIds","expiresAt","now","fetchUserEmailFromToken","errorText","configRedirectUri","targetHost","targetPort","targetProtocol","callbackPath","useConfiguredUri","parsed","redirectUri","hostname","protocol","port","Number","parseInt","pathname","host","path","warn","Promise","resolve","reject","generatePKCE","verifier","codeVerifier","challenge","codeChallenge","server","serverPort","finalRedirectUri","http","createServer","req","res","code","tokenResponse","cachedToken","exchangeError","writeHead","end","getErrorTemplate","close","get","exchangeCodeForToken","access_token","refresh_token","undefined","expires_in","getSuccessTemplate","listen","address","console","open","catch","setTimeout","clientSecret","tokenUrl","params","body","client_id","redirect_uri","grant_type","code_verifier","client_secret","URLSearchParams","method","authMiddleware","wrapAtPosition","module","extraPosition","operation","name","originalHandler","handler","wrappedHandler","allArgs","extra","auth","authRequiredResponse","_meta","authContext","tool","type","content","JSON","stringify","result","structuredContent","withToolAuth","withResourceAuth","withPromptAuth"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/oauth-microsoft/src/providers/loopback-oauth.ts"],"sourcesContent":["/**\n * Loopback OAuth Implementation for Microsoft (RFC 8252)\n *\n * Implements OAuth 2.0 Authorization Code Flow with PKCE using loopback interface redirection.\n * Uses ephemeral local server with OS-assigned port (RFC 8252 Section 8.3).\n * Eliminates port conflicts by using port 0, allowing unlimited concurrent servers.\n *\n * Flow:\n * 1. Check token cache (memory + storage)\n * 2. If cache miss: Start ephemeral server on port 0\n * 3. Generate auth URL with dynamic redirect (localhost:{assigned-port})\n * 4. Open browser or print URL (headless flag controls behavior)\n * 5. Handle callback, exchange code for token\n * 6. Cache token to storage\n * 7. Close ephemeral server\n */\n\nimport { addAccount, generatePKCE, getActiveAccount, getErrorTemplate, getSuccessTemplate, getToken, type OAuth2TokenStorageProvider, setAccountInfo, setActiveAccount, setToken } from '@mcp-z/oauth';\nimport { randomUUID } from 'crypto';\nimport * as http from 'http';\nimport open from 'open';\nimport { fetchWithTimeout } from '../lib/fetch-with-timeout.ts';\nimport { type AuthContext, AuthRequiredError, type CachedToken, type EnrichedExtra, type LoopbackOAuthConfig } from '../types.ts';\n\ninterface TokenResponse {\n access_token: string;\n refresh_token?: string;\n expires_in?: number;\n scope?: string;\n token_type?: string;\n}\n\n/**\n * Loopback OAuth Client (RFC 8252 Section 7.3)\n *\n * Implements OAuth 2.0 Authorization Code Flow with PKCE for native applications\n * using loopback interface redirection. Manages ephemeral OAuth flows and token persistence\n * with Keyv for key-based token storage using compound keys.\n *\n * Token key format: {accountId}:{service}:token (e.g., \"user@example.com:outlook:token\")\n */\nexport class LoopbackOAuthProvider implements OAuth2TokenStorageProvider {\n private config: LoopbackOAuthConfig;\n\n constructor(config: LoopbackOAuthConfig) {\n this.config = config;\n }\n\n /**\n * Get access token from Keyv using compound key\n *\n * @param accountId - Account identifier (email address). Required for loopback OAuth.\n * @returns Access token for API requests\n */\n async getAccessToken(accountId?: string): Promise<string> {\n const { logger, service, tokenStore } = this.config;\n\n // Use active account if no accountId specified\n const effectiveAccountId = accountId ?? (await getActiveAccount(tokenStore, { service }));\n\n // If we have an accountId, try to use existing token\n if (effectiveAccountId) {\n logger.debug('Getting access token', { service, accountId: effectiveAccountId });\n\n // Check Keyv for token using new key format\n const storedToken = await getToken<CachedToken>(tokenStore, { accountId: effectiveAccountId, service });\n\n if (storedToken && this.isTokenValid(storedToken)) {\n logger.debug('Using stored access token', { accountId: effectiveAccountId });\n return storedToken.accessToken;\n }\n\n // If stored token expired but has refresh token, try refresh\n if (storedToken?.refreshToken) {\n try {\n logger.info('Refreshing expired access token', { accountId: effectiveAccountId });\n const refreshedToken = await this.refreshAccessToken(storedToken.refreshToken);\n await setToken(tokenStore, { accountId: effectiveAccountId, service }, refreshedToken);\n return refreshedToken.accessToken;\n } catch (error) {\n logger.info('Token refresh failed, starting new OAuth flow', {\n accountId: effectiveAccountId,\n error: error instanceof Error ? error.message : String(error),\n });\n // Fall through to new OAuth flow\n }\n }\n }\n\n const { clientId, tenantId, scope, redirectUri } = this.config;\n\n if (redirectUri) {\n // Persistent callback mode (cloud deployment with configured redirect_uri)\n const { verifier: codeVerifier, challenge: codeChallenge } = generatePKCE();\n const stateId = randomUUID();\n\n // Store PKCE verifier for callback (5 minute TTL)\n await tokenStore.set(`${service}:pending:${stateId}`, { codeVerifier, createdAt: Date.now() }, 5 * 60 * 1000);\n\n // Build auth URL with configured redirect_uri\n const authUrl = new URL(`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize`);\n authUrl.searchParams.set('client_id', clientId);\n authUrl.searchParams.set('redirect_uri', redirectUri);\n authUrl.searchParams.set('response_type', 'code');\n authUrl.searchParams.set('scope', scope);\n authUrl.searchParams.set('response_mode', 'query');\n authUrl.searchParams.set('code_challenge', codeChallenge);\n authUrl.searchParams.set('code_challenge_method', 'S256');\n authUrl.searchParams.set('state', stateId);\n authUrl.searchParams.set('prompt', 'select_account');\n\n logger.info('OAuth required - persistent callback mode', { service, redirectUri });\n throw new AuthRequiredError({\n kind: 'auth_url',\n provider: service,\n url: authUrl.toString(),\n });\n }\n\n // Ephemeral callback mode (local development)\n logger.info('Starting ephemeral OAuth flow', { service, headless: this.config.headless });\n const { token, email } = await this.performEphemeralOAuthFlow();\n\n await setToken(tokenStore, { accountId: email, service }, token);\n await addAccount(tokenStore, { service, accountId: email });\n await setActiveAccount(tokenStore, { service, accountId: email });\n await setAccountInfo(tokenStore, { service, accountId: email }, { email, addedAt: new Date().toISOString() });\n\n logger.info('OAuth flow completed', { service, accountId: email });\n return token.accessToken;\n }\n\n /**\n * Convert to Microsoft Graph-compatible auth provider\n *\n * @param accountId - Account identifier for multi-account support (e.g., 'user@example.com')\n * @returns Auth provider configured for the specified account\n */\n toAuthProvider(accountId?: string): { getAccessToken: () => Promise<string> } {\n // Capture accountId in closure to pass to getAccessToken\n const getToken = () => this.getAccessToken(accountId);\n\n return {\n getAccessToken: getToken,\n };\n }\n\n /**\n * Get user email from Microsoft Graph API (pure query)\n * Used to query email for existing authenticated account\n *\n * @param accountId - Account identifier to get email for\n * @returns User's email address\n */\n async getUserEmail(accountId?: string): Promise<string> {\n // Get token for existing account\n const token = await this.getAccessToken(accountId);\n\n // Fetch email from Microsoft Graph\n const response = await fetchWithTimeout('https://graph.microsoft.com/v1.0/me', {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!response.ok) {\n throw new Error(`Failed to get user info: ${response.status} ${await response.text()}`);\n }\n\n const userInfo = (await response.json()) as { mail?: string; userPrincipalName: string };\n return userInfo.mail ?? userInfo.userPrincipalName;\n }\n\n private isTokenValid(token: CachedToken): boolean {\n if (!token.expiresAt) return true; // No expiry = assume valid\n return Date.now() < token.expiresAt - 60000; // 1 minute buffer\n }\n\n /**\n * Fetch user email from Microsoft Graph using access token\n * Called during OAuth flow to get email for accountId\n *\n * @param accessToken - Fresh access token from OAuth exchange\n * @returns User's email address (mail field or userPrincipalName fallback)\n */\n private async fetchUserEmailFromToken(accessToken: string): Promise<string> {\n const { logger } = this.config;\n\n const response = await fetchWithTimeout('https://graph.microsoft.com/v1.0/me', {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to fetch user email: HTTP ${response.status} - ${errorText}`);\n }\n\n const userInfo = (await response.json()) as { mail?: string; userPrincipalName: string };\n const email = userInfo.mail ?? userInfo.userPrincipalName;\n\n logger.debug('Fetched user email from Graph API', { email });\n return email;\n }\n\n private async performEphemeralOAuthFlow(): Promise<{ token: CachedToken; email: string }> {\n const { clientId, tenantId, scope, headless, logger, redirectUri: configRedirectUri } = this.config;\n\n // Server listen configuration (where ephemeral server binds)\n let listenHost = 'localhost'; // Default: localhost for ephemeral loopback\n let listenPort = 0; // Default: OS-assigned ephemeral port\n\n // Redirect URI configuration (what goes in auth URL and token exchange)\n let callbackPath = '/callback'; // Default callback path\n let useConfiguredUri = false;\n\n if (configRedirectUri) {\n try {\n const parsed = new URL(configRedirectUri);\n const isLoopback = parsed.hostname === 'localhost' || parsed.hostname === '127.0.0.1';\n\n if (isLoopback) {\n // Local development: Listen on specific loopback address/port\n listenHost = parsed.hostname;\n listenPort = parsed.port ? Number.parseInt(parsed.port, 10) : 0;\n } else {\n // Cloud deployment: Listen on 0.0.0.0 with PORT from environment\n // The redirectUri is the PUBLIC URL (e.g., https://example.com/oauth/callback)\n // The server listens on 0.0.0.0:PORT and the load balancer routes to it\n listenHost = '0.0.0.0';\n const envPort = process.env.PORT ? Number.parseInt(process.env.PORT, 10) : undefined;\n listenPort = envPort && Number.isFinite(envPort) ? envPort : 8080;\n }\n\n // Extract callback path from URL\n if (parsed.pathname && parsed.pathname !== '/') {\n callbackPath = parsed.pathname;\n }\n\n useConfiguredUri = true;\n\n logger.debug('Using configured redirect URI', {\n listenHost,\n listenPort,\n callbackPath,\n redirectUri: configRedirectUri,\n isLoopback,\n });\n } catch (error) {\n logger.warn('Failed to parse redirectUri, using ephemeral defaults', {\n redirectUri: configRedirectUri,\n error: error instanceof Error ? error.message : String(error),\n });\n // Continue with defaults (localhost, port 0, http, /callback)\n }\n }\n\n return new Promise((resolve, reject) => {\n // Generate PKCE challenge\n const { verifier: codeVerifier, challenge: codeChallenge } = generatePKCE();\n\n let server: http.Server | null = null;\n let serverPort: number;\n let finalRedirectUri: string; // Will be set in server.listen callback\n\n // Create ephemeral server with OS-assigned port (RFC 8252)\n server = http.createServer(async (req, res) => {\n if (!req.url) {\n res.writeHead(400, { 'Content-Type': 'text/html' });\n res.end(getErrorTemplate('Invalid request'));\n server?.close();\n reject(new Error('Invalid request: missing URL'));\n return;\n }\n const url = new URL(req.url, `http://localhost:${serverPort}`);\n\n if (url.pathname === callbackPath) {\n const code = url.searchParams.get('code');\n const error = url.searchParams.get('error');\n\n if (error) {\n res.writeHead(400, { 'Content-Type': 'text/html' });\n res.end(getErrorTemplate(error));\n server?.close();\n reject(new Error(`OAuth error: ${error}`));\n return;\n }\n\n if (!code) {\n res.writeHead(400, { 'Content-Type': 'text/html' });\n res.end(getErrorTemplate('No authorization code received'));\n server?.close();\n reject(new Error('No authorization code received'));\n return;\n }\n\n try {\n // Exchange code for token (must use same redirect_uri as in authorization request)\n const tokenResponse = await this.exchangeCodeForToken(code, codeVerifier, finalRedirectUri);\n\n // Build cached token\n const cachedToken: CachedToken = {\n accessToken: tokenResponse.access_token,\n ...(tokenResponse.refresh_token !== undefined && { refreshToken: tokenResponse.refresh_token }),\n ...(tokenResponse.expires_in !== undefined && { expiresAt: Date.now() + tokenResponse.expires_in * 1000 }),\n ...(tokenResponse.scope !== undefined && { scope: tokenResponse.scope }),\n };\n\n // Fetch user email immediately using the new access token\n const email = await this.fetchUserEmailFromToken(tokenResponse.access_token);\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getSuccessTemplate());\n server?.close();\n resolve({ token: cachedToken, email });\n } catch (exchangeError) {\n logger.error('Token exchange failed', { error: exchangeError instanceof Error ? exchangeError.message : String(exchangeError) });\n res.writeHead(500, { 'Content-Type': 'text/html' });\n res.end(getErrorTemplate('Token exchange failed'));\n server?.close();\n reject(exchangeError);\n }\n } else {\n res.writeHead(404, { 'Content-Type': 'text/plain' });\n res.end('Not Found');\n }\n });\n\n // Listen on configured host/port\n // - For loopback (default): localhost with OS-assigned port\n // - For configured loopback: specific localhost port from redirectUri\n // - For cloud deployment: 0.0.0.0:${PORT} from environment\n server.listen(listenPort, listenHost, () => {\n const address = server?.address();\n if (!address || typeof address === 'string') {\n server?.close();\n reject(new Error('Failed to start ephemeral server'));\n return;\n }\n\n serverPort = address.port;\n\n // Construct final redirect URI\n if (useConfiguredUri && configRedirectUri) {\n // Use configured redirect URI as-is (public URL for cloud, or specific local URL)\n finalRedirectUri = configRedirectUri;\n } else {\n // Construct ephemeral redirect URI with actual server port (default local behavior)\n finalRedirectUri = `http://localhost:${serverPort}${callbackPath}`;\n }\n\n // Build Microsoft auth URL\n const authUrl = new URL(`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize`);\n authUrl.searchParams.set('client_id', clientId);\n authUrl.searchParams.set('redirect_uri', finalRedirectUri);\n authUrl.searchParams.set('response_type', 'code');\n authUrl.searchParams.set('scope', scope);\n authUrl.searchParams.set('response_mode', 'query');\n authUrl.searchParams.set('code_challenge', codeChallenge);\n authUrl.searchParams.set('code_challenge_method', 'S256');\n authUrl.searchParams.set('prompt', 'select_account');\n\n logger.info('Ephemeral OAuth server started', { port: serverPort, headless });\n\n if (headless) {\n // Headless mode: Print auth URL to stderr (stdout is MCP protocol)\n console.error('\\nš OAuth Authorization Required');\n console.error('š Please visit this URL in your browser:\\n');\n console.error(` ${authUrl.toString()}\\n`);\n console.error('ā³ Waiting for authorization...\\n');\n } else {\n // Interactive mode: Open browser automatically\n logger.info('Opening browser for OAuth authorization');\n open(authUrl.toString()).catch((error) => {\n logger.info('Failed to open browser automatically', { error: error.message });\n console.error('\\nš OAuth Authorization Required');\n console.error(` ${authUrl.toString()}\\n`);\n });\n }\n });\n\n // Timeout after 5 minutes\n setTimeout(\n () => {\n if (server) {\n server.close();\n reject(new Error('OAuth flow timed out after 5 minutes'));\n }\n },\n 5 * 60 * 1000\n );\n });\n }\n\n private async exchangeCodeForToken(code: string, codeVerifier: string, redirectUri: string): Promise<TokenResponse> {\n const { clientId, clientSecret, tenantId } = this.config;\n\n const tokenUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;\n const params: Record<string, string> = {\n code,\n client_id: clientId,\n redirect_uri: redirectUri,\n grant_type: 'authorization_code',\n code_verifier: codeVerifier,\n };\n\n // Only include client_secret for confidential clients\n if (clientSecret) {\n params.client_secret = clientSecret;\n }\n\n const body = new URLSearchParams(params);\n\n const response = await fetchWithTimeout(tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: body.toString(),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token exchange failed: ${response.status} ${errorText}`);\n }\n\n return (await response.json()) as TokenResponse;\n }\n\n private async refreshAccessToken(refreshToken: string): Promise<CachedToken> {\n const { clientId, clientSecret, tenantId, scope } = this.config;\n\n const tokenUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;\n const params: Record<string, string> = {\n refresh_token: refreshToken,\n client_id: clientId,\n grant_type: 'refresh_token',\n scope,\n };\n\n // Only include client_secret for confidential clients\n if (clientSecret) {\n params.client_secret = clientSecret;\n }\n\n const body = new URLSearchParams(params);\n\n const response = await fetchWithTimeout(tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: body.toString(),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token refresh failed: ${response.status} ${errorText}`);\n }\n\n const tokenResponse = (await response.json()) as TokenResponse;\n\n return {\n accessToken: tokenResponse.access_token,\n refreshToken: refreshToken, // Keep original refresh token\n ...(tokenResponse.expires_in !== undefined && { expiresAt: Date.now() + tokenResponse.expires_in * 1000 }),\n ...(tokenResponse.scope !== undefined && { scope: tokenResponse.scope }),\n };\n }\n\n /**\n * Handle OAuth callback from persistent endpoint.\n * Used by HTTP servers with configured redirectUri.\n *\n * @param params - OAuth callback parameters\n * @returns Email and cached token\n */\n async handleOAuthCallback(params: { code: string; state?: string }): Promise<{ email: string; token: CachedToken }> {\n const { code, state } = params;\n const { logger, service, tokenStore, redirectUri } = this.config;\n\n if (!state) {\n throw new Error('Missing state parameter in OAuth callback');\n }\n\n if (!redirectUri) {\n throw new Error('handleOAuthCallback requires configured redirectUri');\n }\n\n // Load pending auth (includes PKCE verifier)\n const pendingKey = `${service}:pending:${state}`;\n const pendingAuth = await tokenStore.get<{ codeVerifier: string; createdAt: number }>(pendingKey);\n\n if (!pendingAuth) {\n throw new Error('Invalid or expired OAuth state. Please try again.');\n }\n\n // Check TTL (5 minutes)\n if (Date.now() - pendingAuth.createdAt > 5 * 60 * 1000) {\n await tokenStore.delete(pendingKey);\n throw new Error('OAuth state expired. Please try again.');\n }\n\n logger.info('Processing OAuth callback', { service, state });\n\n // Exchange code for token\n const tokenResponse = await this.exchangeCodeForToken(code, pendingAuth.codeVerifier, redirectUri);\n\n // Create cached token\n const cachedToken: CachedToken = {\n accessToken: tokenResponse.access_token,\n refreshToken: tokenResponse.refresh_token,\n expiresAt: tokenResponse.expires_in ? Date.now() + tokenResponse.expires_in * 1000 : undefined,\n ...(tokenResponse.scope !== undefined && { scope: tokenResponse.scope }),\n };\n\n // Fetch user email\n const email = await this.fetchUserEmailFromToken(tokenResponse.access_token);\n\n // Store token\n await setToken(tokenStore, { accountId: email, service }, cachedToken);\n\n // Add account and set as active\n await addAccount(tokenStore, { service, accountId: email });\n await setActiveAccount(tokenStore, { service, accountId: email });\n\n // Store account metadata\n await setAccountInfo(\n tokenStore,\n { service, accountId: email },\n {\n email,\n addedAt: new Date().toISOString(),\n }\n );\n\n // Clean up pending auth\n await tokenStore.delete(pendingKey);\n\n logger.info('OAuth callback completed', { service, email });\n\n return { email, token: cachedToken };\n }\n\n /**\n * Create auth middleware for single-user context (single active account per service)\n *\n * Single-user mode:\n * - Maintains per-service active accounts in storage\n * - Supports backchannel account override via extra._meta.accountId\n * - Automatically enhances output schemas with auth_required branch\n *\n * Example:\n * ```typescript\n * const loopback = new LoopbackOAuthProvider({ service: 'outlook', ... });\n * const middleware = loopback.authMiddleware();\n * const tools = toolFactories.map(f => f()).map(middleware.withToolAuth);\n * const resources = resourceFactories.map(f => f()).map(middleware.withResourceAuth);\n * const prompts = promptFactories.map(f => f()).map(middleware.withPromptAuth);\n * ```\n *\n * @returns Object with withToolAuth, withResourceAuth, withPromptAuth methods\n */\n authMiddleware() {\n const { service, tokenStore, logger } = this.config;\n\n // Shared wrapper logic - extracts extra parameter from specified position\n // Generic T captures the actual module type; handler is cast from unknown to callable\n const wrapAtPosition = <T extends { name: string; handler: unknown; [key: string]: unknown }>(module: T, extraPosition: number): T => {\n const operation = module.name;\n const originalHandler = module.handler as (...args: unknown[]) => Promise<unknown>;\n\n const wrappedHandler = async (...allArgs: unknown[]) => {\n // Extract extra from the correct position (defensive: handle arg-less tool pattern)\n let extra: EnrichedExtra;\n if (allArgs.length <= extraPosition) {\n // Arg-less tool pattern: keep args as-is, create separate extra object\n extra = (allArgs[0] && typeof allArgs[0] === 'object' ? {} : {}) as EnrichedExtra;\n allArgs[extraPosition] = extra;\n } else {\n extra = (allArgs[extraPosition] || {}) as EnrichedExtra;\n allArgs[extraPosition] = extra;\n }\n\n try {\n // Check for backchannel override via _meta.accountId\n let accountId: string | undefined;\n try {\n accountId = extra._meta?.accountId ?? (await getActiveAccount(tokenStore, { service }));\n } catch (error) {\n if (error instanceof Error && ((error as { code?: string }).code === 'REQUIRES_AUTHENTICATION' || error.name === 'AccountManagerError')) {\n accountId = undefined;\n } else {\n throw error;\n }\n }\n\n // Eagerly validate token exists or trigger OAuth flow\n await this.getAccessToken(accountId);\n\n // After OAuth flow completes, get the actual accountId (email) that was set\n const effectiveAccountId = accountId ?? (await getActiveAccount(tokenStore, { service }));\n if (!effectiveAccountId) {\n throw new Error(`No account found after OAuth flow for service ${service}`);\n }\n\n const auth = this.toAuthProvider(effectiveAccountId);\n\n // Inject authContext and logger into extra\n (extra as { authContext?: AuthContext }).authContext = {\n auth,\n accountId: effectiveAccountId,\n };\n (extra as { logger?: unknown }).logger = logger;\n\n // Call original handler with all args\n return await originalHandler(...allArgs);\n } catch (error) {\n // Token retrieval/refresh failed - return auth required\n if (error instanceof AuthRequiredError) {\n logger.info('Authentication required', {\n service,\n tool: operation,\n descriptor: error.descriptor,\n });\n\n // Return auth_required response wrapped in { result } to match tool outputSchema pattern\n // Tools define outputSchema: z.object({ result: discriminatedUnion(...) }) where auth_required is a branch\n const authRequiredResponse = {\n type: 'auth_required' as const,\n provider: service,\n message: `Authentication required for ${operation}. Please authenticate with ${service}.`,\n url: error.descriptor.kind === 'auth_url' ? error.descriptor.url : undefined,\n };\n\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify({ result: authRequiredResponse }),\n },\n ],\n structuredContent: { result: authRequiredResponse },\n };\n }\n\n // Other errors - propagate\n throw error;\n }\n };\n\n return {\n ...module,\n handler: wrappedHandler,\n } as T;\n };\n\n return {\n // Use structural constraints to avoid contravariance check on handler type.\n // wrapAtPosition is now generic and returns T directly.\n withToolAuth: <T extends { name: string; config: unknown; handler: unknown }>(module: T) => wrapAtPosition(module, 1),\n withResourceAuth: <T extends { name: string; template?: unknown; config?: unknown; handler: unknown }>(module: T) => wrapAtPosition(module, 2),\n withPromptAuth: <T extends { name: string; config: unknown; handler: unknown }>(module: T) => wrapAtPosition(module, 0),\n };\n }\n}\n\n/**\n * Create a loopback OAuth client for Microsoft services\n * Works for both stdio and HTTP transports\n */\nexport function createMicrosoftFileAuth(config: LoopbackOAuthConfig): OAuth2TokenStorageProvider {\n return new LoopbackOAuthProvider(config);\n}\n"],"names":["LoopbackOAuthProvider","createMicrosoftFileAuth","config","getAccessToken","accountId","logger","service","tokenStore","effectiveAccountId","storedToken","refreshedToken","error","clientId","tenantId","scope","redirectUri","generatePKCE","codeVerifier","codeChallenge","stateId","authUrl","token","email","getActiveAccount","debug","getToken","isTokenValid","accessToken","refreshToken","info","refreshAccessToken","setToken","Error","message","String","verifier","challenge","randomUUID","set","createdAt","Date","now","URL","searchParams","AuthRequiredError","kind","provider","url","toString","headless","performEphemeralOAuthFlow","addAccount","setActiveAccount","setAccountInfo","addedAt","toISOString","toAuthProvider","getUserEmail","userInfo","response","fetchWithTimeout","headers","Authorization","ok","status","text","json","mail","userPrincipalName","expiresAt","fetchUserEmailFromToken","errorText","configRedirectUri","listenHost","listenPort","callbackPath","useConfiguredUri","parsed","isLoopback","envPort","hostname","port","Number","parseInt","process","env","PORT","undefined","isFinite","pathname","warn","Promise","resolve","reject","server","serverPort","finalRedirectUri","http","createServer","req","res","code","tokenResponse","cachedToken","exchangeError","writeHead","end","getErrorTemplate","close","get","exchangeCodeForToken","access_token","refresh_token","expires_in","getSuccessTemplate","listen","address","console","open","catch","setTimeout","clientSecret","tokenUrl","params","body","client_id","redirect_uri","grant_type","code_verifier","client_secret","URLSearchParams","method","handleOAuthCallback","state","pendingKey","pendingAuth","delete","authMiddleware","wrapAtPosition","module","extraPosition","operation","name","originalHandler","handler","wrappedHandler","allArgs","extra","auth","authRequiredResponse","length","_meta","authContext","tool","descriptor","type","content","JSON","stringify","result","structuredContent","withToolAuth","withResourceAuth","withPromptAuth"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/oauth-microsoft/src/schemas/index.ts"],"sourcesContent":["import { z } from 'zod';\n\nexport const AuthRequiredBranchSchema = z.object({\n type: z.literal('auth_required'),\n provider: z.string(),\n message: z.string(),\n url: z.string().optional(),\n});\nexport type AuthRequiredBranch = z.infer<typeof AuthRequiredBranchSchema>;\n\nexport const AuthRequiredSchema = z\n .object({\n type: z.literal('auth_required'),\n provider: z.string().describe('OAuth provider name (e.g., \"google\")'),\n message: z.string().describe('Human-readable message explaining why auth is needed'),\n url: z.string().url().describe('Authentication URL to open in browser'),\n flow: z.string().optional().describe('Authentication flow type (e.g., \"auth_url\", \"device_code\")'),\n instructions: z.string().describe('Clear instructions for the user'),\n user_code: z.string().optional().describe('Code user must enter at verification URL (device flows only)'),\n expires_in: z.number().optional().describe('Seconds until code expires (device flows only)'),\n accountId: z.string().optional().describe('Account identifier (email) that requires authentication'),\n })\n .describe('Authentication required with clear actionable instructions for user');\n\nexport type AuthRequired = z.infer<typeof AuthRequiredSchema>;\n"],"names":["AuthRequiredBranchSchema","AuthRequiredSchema","z","object","type","literal","provider","string","message","url","optional","describe","flow","instructions","user_code","expires_in","number","accountId"],"mappings":";;;;;;;;;;;QAEaA;eAAAA;;QAQAC;eAAAA;;;mBAVK;AAEX,IAAMD,2BAA2BE,MAAC,CAACC,MAAM,CAAC;IAC/CC,MAAMF,MAAC,CAACG,OAAO,CAAC;IAChBC,UAAUJ,MAAC,CAACK,MAAM;IAClBC,SAASN,MAAC,CAACK,MAAM;IACjBE,KAAKP,MAAC,CAACK,MAAM,GAAGG,QAAQ;AAC1B;AAGO,IAAMT,qBAAqBC,MAAC,CAChCC,MAAM,CAAC;IACNC,MAAMF,MAAC,CAACG,OAAO,CAAC;IAChBC,UAAUJ,MAAC,CAACK,MAAM,GAAGI,QAAQ,CAAC;IAC9BH,SAASN,MAAC,CAACK,MAAM,GAAGI,QAAQ,CAAC;IAC7BF,KAAKP,MAAC,CAACK,MAAM,GAAGE,GAAG,GAAGE,QAAQ,CAAC;IAC/BC,MAAMV,MAAC,CAACK,MAAM,GAAGG,QAAQ,GAAGC,QAAQ,CAAC;IACrCE,cAAcX,MAAC,CAACK,MAAM,GAAGI,QAAQ,CAAC;IAClCG,WAAWZ,MAAC,CAACK,MAAM,GAAGG,QAAQ,GAAGC,QAAQ,CAAC;IAC1CI,YAAYb,MAAC,CAACc,MAAM,GAAGN,QAAQ,GAAGC,QAAQ,CAAC;IAC3CM,WAAWf,MAAC,CAACK,MAAM,GAAGG,QAAQ,GAAGC,QAAQ,CAAC;AAC5C,GACCA,QAAQ,CAAC"}
|
|
@@ -9,8 +9,11 @@ import type { DcrConfig, OAuthConfig } from '../types.js';
|
|
|
9
9
|
export type { DcrConfig, OAuthConfig };
|
|
10
10
|
/**
|
|
11
11
|
* Transport type for MCP servers
|
|
12
|
+
*
|
|
13
|
+
* - 'stdio': Standard input/output transport
|
|
14
|
+
* - 'http': HTTP transport
|
|
12
15
|
*/
|
|
13
|
-
type TransportType = 'stdio' | 'http';
|
|
16
|
+
export type TransportType = 'stdio' | 'http';
|
|
14
17
|
/**
|
|
15
18
|
* Parse Microsoft OAuth configuration from CLI arguments and environment variables.
|
|
16
19
|
*
|
|
@@ -9,8 +9,11 @@ import type { DcrConfig, OAuthConfig } from '../types.js';
|
|
|
9
9
|
export type { DcrConfig, OAuthConfig };
|
|
10
10
|
/**
|
|
11
11
|
* Transport type for MCP servers
|
|
12
|
+
*
|
|
13
|
+
* - 'stdio': Standard input/output transport
|
|
14
|
+
* - 'http': HTTP transport
|
|
12
15
|
*/
|
|
13
|
-
type TransportType = 'stdio' | 'http';
|
|
16
|
+
export type TransportType = 'stdio' | 'http';
|
|
14
17
|
/**
|
|
15
18
|
* Parse Microsoft OAuth configuration from CLI arguments and environment variables.
|
|
16
19
|
*
|
package/dist/cjs/setup/config.js
CHANGED
|
@@ -153,6 +153,9 @@ function parseConfig(args, env, transport) {
|
|
|
153
153
|
var cliRedirectUri = typeof values['redirect-uri'] === 'string' ? values['redirect-uri'] : undefined;
|
|
154
154
|
var envRedirectUri = env.REDIRECT_URI;
|
|
155
155
|
var redirectUri = cliRedirectUri !== null && cliRedirectUri !== void 0 ? cliRedirectUri : envRedirectUri;
|
|
156
|
+
if (redirectUri && transport === 'stdio') {
|
|
157
|
+
throw new Error('REDIRECT_URI requires HTTP transport. The OAuth callback must be served over HTTP.');
|
|
158
|
+
}
|
|
156
159
|
// Parse tenant-id (CLI overrides environment)
|
|
157
160
|
var cliTenantId = typeof values['tenant-id'] === 'string' ? values['tenant-id'] : undefined;
|
|
158
161
|
var tenantId = cliTenantId !== null && cliTenantId !== void 0 ? cliTenantId : requiredEnv('MS_TENANT_ID');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/oauth/oauth-microsoft/src/setup/config.ts"],"sourcesContent":["/**\n * Microsoft OAuth configuration parsing from CLI arguments and environment variables.\n *\n * This module provides utilities to parse Microsoft OAuth configuration from\n * CLI arguments and environment variables, following the same pattern as @mcp-z/server's\n * parseConfig().\n */\n\nimport { parseArgs } from 'util';\nimport type { DcrConfig, OAuthConfig } from '../types.ts';\n\n// Re-export for external use\nexport type { DcrConfig, OAuthConfig };\n\n/**\n * Auth mode type (from OAuthConfig)\n */\ntype AuthMode = 'loopback-oauth' | 'device-code' | 'dcr';\n\n/**\n * Parse OAuth mode string into auth mode.\n *\n * @param value - OAuth mode string ('loopback-oauth', 'device-code', or 'dcr')\n * @returns Parsed auth mode\n * @throws Error if value is invalid\n *\n * @example Valid formats\n * ```typescript\n * parseAuthMode('loopback-oauth') // { auth: 'loopback-oauth' }\n * parseAuthMode('device-code') // { auth: 'device-code' }\n * parseAuthMode('dcr') // { auth: 'dcr' }\n * ```\n */\nfunction parseAuthMode(value: string): {\n auth: AuthMode;\n} {\n // Validate auth mode\n if (value !== 'loopback-oauth' && value !== 'device-code' && value !== 'dcr') {\n throw new Error(`Invalid --auth value: \"${value}\". Valid values: loopback-oauth, device-code, dcr`);\n }\n\n return {\n auth: value as AuthMode,\n };\n}\n\n/**\n * Transport type for MCP servers\n */\ntype TransportType = 'stdio' | 'http';\n\n/**\n * Parse Microsoft OAuth configuration from CLI arguments and environment variables.\n *\n * CLI Arguments:\n * - --auth: OAuth mode ('loopback-oauth' | 'device-code' | 'dcr')\n * - Default: 'loopback-oauth' (if flag is omitted)\n * - --headless: Disable browser opening for OAuth flow (default: false, true in test env)\n * - --redirect-uri: Override OAuth redirect URI (default: ephemeral loopback)\n * - --tenant-id: Override Microsoft tenant ID\n *\n * Required environment variables:\n * - MS_CLIENT_ID: Azure AD application (client) ID\n * - MS_TENANT_ID: Azure AD tenant ID ('common', 'organizations', 'consumers', or tenant GUID)\n *\n * Optional environment variables:\n * - MS_CLIENT_SECRET: Azure AD client secret (optional for public clients)\n * - AUTH_MODE: OAuth mode (same format as --auth flag)\n * - HEADLESS: Headless mode flag ('true' to enable)\n * - REDIRECT_URI: OAuth redirect URI (overridden by --redirect-uri CLI flag)\n *\n * @param args - CLI arguments array (typically process.argv)\n * @param env - Environment variables object (typically process.env)\n * @param transport - Optional transport type. If 'stdio' and auth mode is 'dcr', throws an error.\n * @returns Parsed Microsoft OAuth configuration\n * @throws Error if required environment variables are missing, values are invalid, or DCR is used with stdio transport\n *\n * @example Default mode (no flags)\n * ```typescript\n * const config = parseConfig(process.argv, process.env);\n * // { auth: 'loopback-oauth' }\n * ```\n *\n * @example Override auth mode\n * ```typescript\n * parseConfig(['--auth=loopback-oauth'], process.env);\n * parseConfig(['--auth=device-code'], process.env);\n * ```\n *\n * @example With transport validation\n * ```typescript\n * parseConfig(['--auth=dcr'], process.env, 'http'); // OK\n * parseConfig(['--auth=dcr'], process.env, 'stdio'); // Throws error\n * ```\n *\n * Valid auth modes:\n * - loopback-oauth (default)\n * - device-code\n * - dcr (HTTP transport only)\n */\nexport function parseConfig(args: string[], env: Record<string, string | undefined>, transport?: TransportType): OAuthConfig {\n function requiredEnv(key: string): string {\n const value = env[key];\n if (!value) {\n throw new Error(`Environment variable ${key} is required for Microsoft OAuth`);\n }\n return value;\n }\n\n // Parse CLI arguments\n const { values } = parseArgs({\n args,\n options: {\n auth: { type: 'string' },\n headless: { type: 'boolean' },\n 'redirect-uri': { type: 'string' },\n 'tenant-id': { type: 'string' },\n },\n strict: false, // Allow other arguments\n allowPositionals: true,\n });\n\n // Parse OAuth mode\n const authArg = typeof values.auth === 'string' ? values.auth : undefined;\n const envAuthMode = env.AUTH_MODE;\n const mode = authArg || envAuthMode;\n\n let auth: AuthMode;\n\n if (mode) {\n const parsed = parseAuthMode(mode);\n auth = parsed.auth;\n } else {\n // DEFAULT: No flags provided, use loopback-oauth\n auth = 'loopback-oauth';\n }\n\n // Validate: DCR only works with HTTP transport\n if (auth === 'dcr' && transport === 'stdio') {\n throw new Error('DCR authentication mode requires HTTP transport. DCR is not supported with stdio transport.');\n }\n\n // Parse headless mode\n const cliHeadless = typeof values.headless === 'boolean' ? values.headless : undefined;\n const envHeadless = env.HEADLESS === 'true' ? true : env.HEADLESS === 'false' ? false : undefined;\n const headless = cliHeadless ?? envHeadless ?? false;\n\n // Parse redirect-uri (CLI overrides ENV)\n const cliRedirectUri = typeof values['redirect-uri'] === 'string' ? values['redirect-uri'] : undefined;\n const envRedirectUri = env.REDIRECT_URI;\n const redirectUri = cliRedirectUri ?? envRedirectUri;\n\n // Parse tenant-id (CLI overrides environment)\n const cliTenantId = typeof values['tenant-id'] === 'string' ? values['tenant-id'] : undefined;\n const tenantId = cliTenantId ?? requiredEnv('MS_TENANT_ID');\n\n // Parse credentials\n const clientId = requiredEnv('MS_CLIENT_ID');\n const clientSecret = env.MS_CLIENT_SECRET;\n\n return {\n clientId,\n tenantId,\n ...(clientSecret && { clientSecret }),\n auth,\n headless,\n ...(redirectUri && { redirectUri }),\n };\n}\n\n/**\n * Build production configuration from process globals.\n * Entry point for production server.\n */\nexport function createConfig(): OAuthConfig {\n return parseConfig(process.argv, process.env);\n}\n\n/**\n * Parse DCR configuration from CLI arguments and environment variables.\n *\n * CLI Arguments:\n * - --dcr-mode: DCR mode ('self-hosted' | 'external')\n * - Default: 'self-hosted' (if flag is omitted)\n * - --dcr-verify-url: External verification endpoint URL (required for external mode)\n * - --dcr-store-uri: DCR client storage URI (required for self-hosted mode)\n * - --tenant-id: Override Microsoft tenant ID\n *\n * Required environment variables:\n * - MS_CLIENT_ID: Azure AD application (client) ID\n * - MS_TENANT_ID: Azure AD tenant ID ('common', 'organizations', 'consumers', or tenant GUID)\n *\n * Optional environment variables:\n * - MS_CLIENT_SECRET: Azure AD client secret (optional for public clients)\n * - DCR_MODE: DCR mode (same format as --dcr-mode flag)\n * - DCR_VERIFY_URL: External verification URL (same as --dcr-verify-url flag)\n * - DCR_STORE_URI: DCR storage URI (same as --dcr-store-uri flag)\n *\n * @param args - CLI arguments array (typically process.argv)\n * @param env - Environment variables object (typically process.env)\n * @param scope - OAuth scopes to request (space-separated)\n * @returns Parsed DCR configuration\n * @throws Error if required environment variables are missing or validation fails\n *\n * @example Self-hosted mode\n * ```typescript\n * const config = parseDcrConfig(\n * ['--dcr-mode=self-hosted', '--dcr-store-uri=file:///path/to/store.json'],\n * process.env,\n * 'https://graph.microsoft.com/.default'\n * );\n * ```\n *\n * @example External mode\n * ```typescript\n * const config = parseDcrConfig(\n * ['--dcr-mode=external', '--dcr-verify-url=https://auth0.example.com/verify'],\n * process.env,\n * 'https://graph.microsoft.com/.default'\n * );\n * ```\n */\nexport function parseDcrConfig(args: string[], env: Record<string, string | undefined>, scope: string): DcrConfig {\n function requiredEnv(key: string): string {\n const value = env[key];\n if (!value) {\n throw new Error(`Environment variable ${key} is required for DCR configuration`);\n }\n return value;\n }\n\n // Parse CLI arguments\n const { values } = parseArgs({\n args,\n options: {\n 'dcr-mode': { type: 'string' },\n 'dcr-verify-url': { type: 'string' },\n 'dcr-store-uri': { type: 'string' },\n 'tenant-id': { type: 'string' },\n },\n strict: false, // Allow other arguments\n allowPositionals: true,\n });\n\n // Parse DCR mode (CLI overrides environment)\n const cliMode = typeof values['dcr-mode'] === 'string' ? values['dcr-mode'] : undefined;\n const envMode = env.DCR_MODE;\n const mode = cliMode || envMode || 'self-hosted';\n\n // Validate DCR mode\n if (mode !== 'self-hosted' && mode !== 'external') {\n throw new Error(`Invalid --dcr-mode value: \"${mode}\". Valid values: self-hosted, external`);\n }\n\n // Parse verify URL (CLI overrides environment)\n const cliVerifyUrl = typeof values['dcr-verify-url'] === 'string' ? values['dcr-verify-url'] : undefined;\n const envVerifyUrl = env.DCR_VERIFY_URL;\n const verifyUrl = cliVerifyUrl || envVerifyUrl;\n\n // Parse store URI (CLI overrides environment)\n const cliStoreUri = typeof values['dcr-store-uri'] === 'string' ? values['dcr-store-uri'] : undefined;\n const envStoreUri = env.DCR_STORE_URI;\n const storeUri = cliStoreUri || envStoreUri;\n\n // Validate mode-specific required fields\n if (mode === 'external' && !verifyUrl) {\n throw new Error('DCR external mode requires --dcr-verify-url or DCR_VERIFY_URL environment variable');\n }\n\n // Parse tenant-id (CLI overrides environment)\n const cliTenantId = typeof values['tenant-id'] === 'string' ? values['tenant-id'] : undefined;\n const tenantId = cliTenantId ?? requiredEnv('MS_TENANT_ID');\n\n // Parse credentials\n const clientId = requiredEnv('MS_CLIENT_ID');\n const clientSecret = env.MS_CLIENT_SECRET;\n\n return {\n mode,\n ...(verifyUrl && { verifyUrl }),\n ...(storeUri && { storeUri }),\n clientId,\n ...(clientSecret && { clientSecret }),\n tenantId,\n scope,\n };\n}\n"],"names":["createConfig","parseConfig","parseDcrConfig","parseAuthMode","value","Error","auth","args","env","transport","cliHeadless","requiredEnv","key","values","parseArgs","options","type","headless","strict","allowPositionals","authArg","undefined","envAuthMode","AUTH_MODE","mode","parsed","envHeadless","HEADLESS","cliRedirectUri","envRedirectUri","REDIRECT_URI","redirectUri","cliTenantId","tenantId","clientId","clientSecret","MS_CLIENT_SECRET","process","argv","scope","cliMode","envMode","DCR_MODE","cliVerifyUrl","envVerifyUrl","DCR_VERIFY_URL","verifyUrl","cliStoreUri","envStoreUri","DCR_STORE_URI","storeUri"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/oauth-microsoft/src/setup/config.ts"],"sourcesContent":["/**\n * Microsoft OAuth configuration parsing from CLI arguments and environment variables.\n *\n * This module provides utilities to parse Microsoft OAuth configuration from\n * CLI arguments and environment variables, following the same pattern as @mcp-z/server's\n * parseConfig().\n */\n\nimport { parseArgs } from 'util';\nimport type { DcrConfig, OAuthConfig } from '../types.ts';\n\n// Re-export for external use\nexport type { DcrConfig, OAuthConfig };\n\n/**\n * Auth mode type (from OAuthConfig)\n */\ntype AuthMode = 'loopback-oauth' | 'device-code' | 'dcr';\n\n/**\n * Parse OAuth mode string into auth mode.\n *\n * @param value - OAuth mode string ('loopback-oauth', 'device-code', or 'dcr')\n * @returns Parsed auth mode\n * @throws Error if value is invalid\n *\n * @example Valid formats\n * ```typescript\n * parseAuthMode('loopback-oauth') // { auth: 'loopback-oauth' }\n * parseAuthMode('device-code') // { auth: 'device-code' }\n * parseAuthMode('dcr') // { auth: 'dcr' }\n * ```\n */\nfunction parseAuthMode(value: string): {\n auth: AuthMode;\n} {\n // Validate auth mode\n if (value !== 'loopback-oauth' && value !== 'device-code' && value !== 'dcr') {\n throw new Error(`Invalid --auth value: \"${value}\". Valid values: loopback-oauth, device-code, dcr`);\n }\n\n return {\n auth: value as AuthMode,\n };\n}\n\n/**\n * Transport type for MCP servers\n *\n * - 'stdio': Standard input/output transport\n * - 'http': HTTP transport\n */\nexport type TransportType = 'stdio' | 'http';\n\n/**\n * Parse Microsoft OAuth configuration from CLI arguments and environment variables.\n *\n * CLI Arguments:\n * - --auth: OAuth mode ('loopback-oauth' | 'device-code' | 'dcr')\n * - Default: 'loopback-oauth' (if flag is omitted)\n * - --headless: Disable browser opening for OAuth flow (default: false, true in test env)\n * - --redirect-uri: Override OAuth redirect URI (default: ephemeral loopback)\n * - --tenant-id: Override Microsoft tenant ID\n *\n * Required environment variables:\n * - MS_CLIENT_ID: Azure AD application (client) ID\n * - MS_TENANT_ID: Azure AD tenant ID ('common', 'organizations', 'consumers', or tenant GUID)\n *\n * Optional environment variables:\n * - MS_CLIENT_SECRET: Azure AD client secret (optional for public clients)\n * - AUTH_MODE: OAuth mode (same format as --auth flag)\n * - HEADLESS: Headless mode flag ('true' to enable)\n * - REDIRECT_URI: OAuth redirect URI (overridden by --redirect-uri CLI flag)\n *\n * @param args - CLI arguments array (typically process.argv)\n * @param env - Environment variables object (typically process.env)\n * @param transport - Optional transport type. If 'stdio' and auth mode is 'dcr', throws an error.\n * @returns Parsed Microsoft OAuth configuration\n * @throws Error if required environment variables are missing, values are invalid, or DCR is used with stdio transport\n *\n * @example Default mode (no flags)\n * ```typescript\n * const config = parseConfig(process.argv, process.env);\n * // { auth: 'loopback-oauth' }\n * ```\n *\n * @example Override auth mode\n * ```typescript\n * parseConfig(['--auth=loopback-oauth'], process.env);\n * parseConfig(['--auth=device-code'], process.env);\n * ```\n *\n * @example With transport validation\n * ```typescript\n * parseConfig(['--auth=dcr'], process.env, 'http'); // OK\n * parseConfig(['--auth=dcr'], process.env, 'stdio'); // Throws error\n * ```\n *\n * Valid auth modes:\n * - loopback-oauth (default)\n * - device-code\n * - dcr (HTTP transport only)\n */\nexport function parseConfig(args: string[], env: Record<string, string | undefined>, transport?: TransportType): OAuthConfig {\n function requiredEnv(key: string): string {\n const value = env[key];\n if (!value) {\n throw new Error(`Environment variable ${key} is required for Microsoft OAuth`);\n }\n return value;\n }\n\n // Parse CLI arguments\n const { values } = parseArgs({\n args,\n options: {\n auth: { type: 'string' },\n headless: { type: 'boolean' },\n 'redirect-uri': { type: 'string' },\n 'tenant-id': { type: 'string' },\n },\n strict: false, // Allow other arguments\n allowPositionals: true,\n });\n\n // Parse OAuth mode\n const authArg = typeof values.auth === 'string' ? values.auth : undefined;\n const envAuthMode = env.AUTH_MODE;\n const mode = authArg || envAuthMode;\n\n let auth: AuthMode;\n\n if (mode) {\n const parsed = parseAuthMode(mode);\n auth = parsed.auth;\n } else {\n // DEFAULT: No flags provided, use loopback-oauth\n auth = 'loopback-oauth';\n }\n\n // Validate: DCR only works with HTTP transport\n if (auth === 'dcr' && transport === 'stdio') {\n throw new Error('DCR authentication mode requires HTTP transport. DCR is not supported with stdio transport.');\n }\n\n // Parse headless mode\n const cliHeadless = typeof values.headless === 'boolean' ? values.headless : undefined;\n const envHeadless = env.HEADLESS === 'true' ? true : env.HEADLESS === 'false' ? false : undefined;\n const headless = cliHeadless ?? envHeadless ?? false;\n\n // Parse redirect-uri (CLI overrides ENV)\n const cliRedirectUri = typeof values['redirect-uri'] === 'string' ? values['redirect-uri'] : undefined;\n const envRedirectUri = env.REDIRECT_URI;\n const redirectUri = cliRedirectUri ?? envRedirectUri;\n if (redirectUri && transport === 'stdio') {\n throw new Error('REDIRECT_URI requires HTTP transport. The OAuth callback must be served over HTTP.');\n }\n\n // Parse tenant-id (CLI overrides environment)\n const cliTenantId = typeof values['tenant-id'] === 'string' ? values['tenant-id'] : undefined;\n const tenantId = cliTenantId ?? requiredEnv('MS_TENANT_ID');\n\n // Parse credentials\n const clientId = requiredEnv('MS_CLIENT_ID');\n const clientSecret = env.MS_CLIENT_SECRET;\n\n return {\n clientId,\n tenantId,\n ...(clientSecret && { clientSecret }),\n auth,\n headless,\n ...(redirectUri && { redirectUri }),\n };\n}\n\n/**\n * Build production configuration from process globals.\n * Entry point for production server.\n */\nexport function createConfig(): OAuthConfig {\n return parseConfig(process.argv, process.env);\n}\n\n/**\n * Parse DCR configuration from CLI arguments and environment variables.\n *\n * CLI Arguments:\n * - --dcr-mode: DCR mode ('self-hosted' | 'external')\n * - Default: 'self-hosted' (if flag is omitted)\n * - --dcr-verify-url: External verification endpoint URL (required for external mode)\n * - --dcr-store-uri: DCR client storage URI (required for self-hosted mode)\n * - --tenant-id: Override Microsoft tenant ID\n *\n * Required environment variables:\n * - MS_CLIENT_ID: Azure AD application (client) ID\n * - MS_TENANT_ID: Azure AD tenant ID ('common', 'organizations', 'consumers', or tenant GUID)\n *\n * Optional environment variables:\n * - MS_CLIENT_SECRET: Azure AD client secret (optional for public clients)\n * - DCR_MODE: DCR mode (same format as --dcr-mode flag)\n * - DCR_VERIFY_URL: External verification URL (same as --dcr-verify-url flag)\n * - DCR_STORE_URI: DCR storage URI (same as --dcr-store-uri flag)\n *\n * @param args - CLI arguments array (typically process.argv)\n * @param env - Environment variables object (typically process.env)\n * @param scope - OAuth scopes to request (space-separated)\n * @returns Parsed DCR configuration\n * @throws Error if required environment variables are missing or validation fails\n *\n * @example Self-hosted mode\n * ```typescript\n * const config = parseDcrConfig(\n * ['--dcr-mode=self-hosted', '--dcr-store-uri=file:///path/to/store.json'],\n * process.env,\n * 'https://graph.microsoft.com/.default'\n * );\n * ```\n *\n * @example External mode\n * ```typescript\n * const config = parseDcrConfig(\n * ['--dcr-mode=external', '--dcr-verify-url=https://auth0.example.com/verify'],\n * process.env,\n * 'https://graph.microsoft.com/.default'\n * );\n * ```\n */\nexport function parseDcrConfig(args: string[], env: Record<string, string | undefined>, scope: string): DcrConfig {\n function requiredEnv(key: string): string {\n const value = env[key];\n if (!value) {\n throw new Error(`Environment variable ${key} is required for DCR configuration`);\n }\n return value;\n }\n\n // Parse CLI arguments\n const { values } = parseArgs({\n args,\n options: {\n 'dcr-mode': { type: 'string' },\n 'dcr-verify-url': { type: 'string' },\n 'dcr-store-uri': { type: 'string' },\n 'tenant-id': { type: 'string' },\n },\n strict: false, // Allow other arguments\n allowPositionals: true,\n });\n\n // Parse DCR mode (CLI overrides environment)\n const cliMode = typeof values['dcr-mode'] === 'string' ? values['dcr-mode'] : undefined;\n const envMode = env.DCR_MODE;\n const mode = cliMode || envMode || 'self-hosted';\n\n // Validate DCR mode\n if (mode !== 'self-hosted' && mode !== 'external') {\n throw new Error(`Invalid --dcr-mode value: \"${mode}\". Valid values: self-hosted, external`);\n }\n\n // Parse verify URL (CLI overrides environment)\n const cliVerifyUrl = typeof values['dcr-verify-url'] === 'string' ? values['dcr-verify-url'] : undefined;\n const envVerifyUrl = env.DCR_VERIFY_URL;\n const verifyUrl = cliVerifyUrl || envVerifyUrl;\n\n // Parse store URI (CLI overrides environment)\n const cliStoreUri = typeof values['dcr-store-uri'] === 'string' ? values['dcr-store-uri'] : undefined;\n const envStoreUri = env.DCR_STORE_URI;\n const storeUri = cliStoreUri || envStoreUri;\n\n // Validate mode-specific required fields\n if (mode === 'external' && !verifyUrl) {\n throw new Error('DCR external mode requires --dcr-verify-url or DCR_VERIFY_URL environment variable');\n }\n\n // Parse tenant-id (CLI overrides environment)\n const cliTenantId = typeof values['tenant-id'] === 'string' ? values['tenant-id'] : undefined;\n const tenantId = cliTenantId ?? requiredEnv('MS_TENANT_ID');\n\n // Parse credentials\n const clientId = requiredEnv('MS_CLIENT_ID');\n const clientSecret = env.MS_CLIENT_SECRET;\n\n return {\n mode,\n ...(verifyUrl && { verifyUrl }),\n ...(storeUri && { storeUri }),\n clientId,\n ...(clientSecret && { clientSecret }),\n tenantId,\n scope,\n };\n}\n"],"names":["createConfig","parseConfig","parseDcrConfig","parseAuthMode","value","Error","auth","args","env","transport","cliHeadless","requiredEnv","key","values","parseArgs","options","type","headless","strict","allowPositionals","authArg","undefined","envAuthMode","AUTH_MODE","mode","parsed","envHeadless","HEADLESS","cliRedirectUri","envRedirectUri","REDIRECT_URI","redirectUri","cliTenantId","tenantId","clientId","clientSecret","MS_CLIENT_SECRET","process","argv","scope","cliMode","envMode","DCR_MODE","cliVerifyUrl","envVerifyUrl","DCR_VERIFY_URL","verifyUrl","cliStoreUri","envStoreUri","DCR_STORE_URI","storeUri"],"mappings":"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"}
|
package/dist/cjs/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/oauth-microsoft/src/types.ts"],"sourcesContent":["/**\n * Standalone types for Microsoft OAuth\n * No dependencies on other @mcp-z packages except @mcp-z/oauth\n */\n\n// Import shared types from base @mcp-z/oauth package\n// Public types (will be re-exported)\n// Internal-only types (not re-exported, used by providers)\nimport type { AuthFlowDescriptor, CachedToken, DcrClientInformation, DcrClientMetadata, Logger, OAuth2TokenStorageProvider, ProviderTokens, ToolHandler, ToolModule, UserAuthProvider } from '@mcp-z/oauth';\nimport type { RequestHandlerExtra } from '@modelcontextprotocol/sdk/shared/protocol.js';\nimport type { ServerNotification, ServerRequest } from '@modelcontextprotocol/sdk/types.js';\nimport type { Keyv } from 'keyv';\n\n// Re-export only essential shared types for public API\nexport type { Logger, CachedToken, ToolModule, ProviderTokens, DcrClientMetadata, DcrClientInformation };\n\n// Re-export error class\nexport { AuthRequiredError } from '@mcp-z/oauth';\n\n// Make internal types available for internal use without exporting\nexport type { ToolHandler, AuthFlowDescriptor, OAuth2TokenStorageProvider, UserAuthProvider, RequestHandlerExtra, ServerRequest, ServerNotification };\n\n// ============================================================================\n// Core Authentication Types\n// ============================================================================\n\n/**\n * Microsoft service types that support OAuth\n * OAuth clients support all Microsoft services provided by Microsoft Graph\n * @public\n */\nexport type MicrosoftService = string;\n\n// ============================================================================\n// Configuration Types\n// ============================================================================\n\n/**\n * OAuth client configuration for upstream provider\n * @public\n */\nexport interface OAuthClientConfig {\n /** OAuth client ID for upstream provider */\n clientId: string;\n /** OAuth client secret (optional for some flows) */\n clientSecret?: string;\n /** Tenant/directory ID (for multi-tenant providers) */\n tenantId?: string;\n}\n\n/**\n * Microsoft OAuth configuration interface.\n * Contains all OAuth-related configuration from CLI arguments and environment variables.\n * @public\n */\nexport interface OAuthConfig {\n /** OAuth client ID */\n clientId: string;\n /** OAuth client secret (optional for public clients) */\n clientSecret?: string;\n /** Azure AD tenant ID */\n tenantId: string;\n /** OAuth adapter mode */\n auth: 'loopback-oauth' | 'device-code' | 'dcr';\n /** Whether to run in headless mode (no browser interaction) */\n headless: boolean;\n /** Optional redirect URI override (defaults to ephemeral loopback) */\n redirectUri?: string;\n}\n\n/**\n * DCR configuration for dynamic client registration\n * @public\n */\nexport interface DcrConfig {\n /** DCR mode: self-hosted (runs own OAuth server) or external (uses Auth0/Stitch) */\n mode: 'self-hosted' | 'external';\n /** External verification endpoint URL (required for external mode) */\n verifyUrl?: string;\n /** DCR client storage URI (required for self-hosted mode) */\n storeUri?: string;\n /** OAuth client ID for Microsoft Graph */\n clientId: string;\n /** OAuth client secret (optional for public clients) */\n clientSecret?: string;\n /** Azure AD tenant ID */\n tenantId: string;\n /** OAuth scopes to request */\n scope: string;\n /** Logger instance */\n logger?: Logger;\n}\n\n/**\n * Configuration for loopback OAuth client\n * @public\n */\nexport interface LoopbackOAuthConfig {\n /** Microsoft service type (e.g., 'outlook') */\n service: MicrosoftService;\n /** OAuth client ID */\n clientId: string;\n /** OAuth client secret (optional for public clients) */\n clientSecret?: string | undefined;\n /** Azure AD tenant ID */\n tenantId: string;\n /** OAuth scopes to request */\n scope: string;\n /** Whether to run in headless mode (no browser interaction) */\n headless: boolean;\n /** Logger instance */\n logger: Logger;\n /** Token storage */\n tokenStore: Keyv<unknown>;\n /** Optional redirect URI override (defaults to ephemeral loopback) */\n redirectUri?: string;\n}\n\n// ============================================================================\n// Middleware Types\n// ============================================================================\n\n/**\n * Microsoft Graph AuthenticationProvider interface\n * Used by Microsoft Graph SDK for API authentication\n * @public\n */\nexport interface MicrosoftAuthProvider {\n getAccessToken: () => Promise<string>;\n}\n\n/**\n * Auth context injected into extra by middleware\n * @public\n */\nexport interface AuthContext {\n /**\n * Microsoft Graph AuthenticationProvider ready for Graph SDK\n * GUARANTEED to exist when handler runs\n */\n auth: MicrosoftAuthProvider;\n\n /**\n * Account being used (for logging, debugging)\n */\n accountId: string;\n\n /**\n * User ID (multi-tenant only)\n */\n}\n\n/**\n * Enriched extra with guaranteed auth context and logger\n * Handlers receive this type - never plain RequestHandlerExtra\n * @public\n */\nexport interface EnrichedExtra extends RequestHandlerExtra<ServerRequest, ServerNotification> {\n /**\n * Auth context injected by middleware\n * GUARANTEED to exist (middleware catches auth failures)\n */\n authContext: AuthContext;\n\n /**\n * Logger injected by middleware\n * GUARANTEED to exist\n */\n logger: Logger;\n\n /**\n * HTTP request object (for HTTP transport scenarios)\n * Optional - present when using HTTP transport with JWT/session auth\n */\n req?: unknown;\n\n // Preserve backchannel support\n _meta?: {\n accountId?: string;\n [key: string]: unknown;\n };\n}\n\n// ============================================================================\n// DCR Internal Types\n// ============================================================================\n\n/**\n * Registered client with full metadata\n * Extends DcrClientInformation with internal timestamps\n * @internal\n */\nexport interface RegisteredClient extends DcrClientInformation {\n /** Creation timestamp (milliseconds since epoch) */\n created_at: number;\n}\n\n/**\n * Authorization code data structure\n * @public\n */\nexport interface AuthorizationCode {\n code: string;\n client_id: string;\n redirect_uri: string;\n scope: string;\n code_challenge?: string;\n code_challenge_method?: string;\n /** Microsoft provider tokens obtained during authorization */\n providerTokens: ProviderTokens;\n created_at: number;\n expires_at: number;\n}\n\n/**\n * Access token data structure\n * @public\n */\nexport interface AccessToken {\n access_token: string;\n token_type: 'Bearer';\n expires_in: number;\n refresh_token?: string;\n scope: string;\n client_id: string;\n /** Microsoft provider tokens */\n providerTokens: ProviderTokens;\n created_at: number;\n}\n\n// ============================================================================\n// Schema Types\n// ============================================================================\n\n/**\n * Auth required response interface\n * Re-exported from @mcp-z/oauth for consistency\n */\nexport type { AuthRequired, AuthRequiredBranch } from './schemas/index.ts';\n"],"names":["AuthRequiredError"],"mappings":"AAAA;;;CAGC,GAED,qDAAqD;AACrD,qCAAqC;AACrC,2DAA2D;;;;;+BAUlDA;;;eAAAA,wBAAiB;;;qBAAQ"}
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
*/
|
|
8
8
|
export { createDcrRouter, type DcrRouterConfig } from './lib/dcr-router.js';
|
|
9
9
|
export { type VerificationResult, verifyBearerToken } from './lib/dcr-verify.js';
|
|
10
|
+
export { createLoopbackCallbackRouter } from './lib/loopback-router.js';
|
|
10
11
|
export { type AuthInfo, DcrTokenVerifier } from './lib/token-verifier.js';
|
|
11
12
|
export { DcrOAuthProvider, type DcrOAuthProviderConfig } from './providers/dcr.js';
|
|
12
13
|
export { type DeviceCodeConfig, DeviceCodeProvider } from './providers/device-code.js';
|
|
13
14
|
export { LoopbackOAuthProvider } from './providers/loopback-oauth.js';
|
|
14
15
|
export * as schemas from './schemas/index.js';
|
|
15
|
-
export { createConfig, parseConfig, parseDcrConfig } from './setup/config.js';
|
|
16
|
+
export { createConfig, parseConfig, parseDcrConfig, type TransportType } from './setup/config.js';
|
|
16
17
|
export * from './types.js';
|
package/dist/esm/index.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* - Device Code flow (RFC 8628) - For headless/limited-input scenarios
|
|
7
7
|
*/ export { createDcrRouter } from './lib/dcr-router.js';
|
|
8
8
|
export { verifyBearerToken } from './lib/dcr-verify.js';
|
|
9
|
+
export { createLoopbackCallbackRouter } from './lib/loopback-router.js';
|
|
9
10
|
export { DcrTokenVerifier } from './lib/token-verifier.js';
|
|
10
11
|
export { DcrOAuthProvider } from './providers/dcr.js';
|
|
11
12
|
export { DeviceCodeProvider } from './providers/device-code.js';
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/oauth-microsoft/src/index.ts"],"sourcesContent":["/**\n * @mcp-z/oauth-microsoft - Shared Microsoft OAuth implementation\n *\n * Provides OAuth authentication:\n * - Loopback OAuth (RFC 8252) - Server-managed, file-based tokens\n * - Device Code flow (RFC 8628) - For headless/limited-input scenarios\n */\n\nexport { createDcrRouter, type DcrRouterConfig } from './lib/dcr-router.ts';\nexport { type VerificationResult, verifyBearerToken } from './lib/dcr-verify.ts';\nexport { createLoopbackCallbackRouter } from './lib/loopback-router.ts';\nexport { type AuthInfo, DcrTokenVerifier } from './lib/token-verifier.ts';\nexport { DcrOAuthProvider, type DcrOAuthProviderConfig } from './providers/dcr.ts';\nexport { type DeviceCodeConfig, DeviceCodeProvider } from './providers/device-code.ts';\nexport { LoopbackOAuthProvider } from './providers/loopback-oauth.ts';\nexport * as schemas from './schemas/index.ts';\nexport { createConfig, parseConfig, parseDcrConfig, type TransportType } from './setup/config.ts';\nexport * from './types.ts';\n"],"names":["createDcrRouter","verifyBearerToken","createLoopbackCallbackRouter","DcrTokenVerifier","DcrOAuthProvider","DeviceCodeProvider","LoopbackOAuthProvider","schemas","createConfig","parseConfig","parseDcrConfig"],"mappings":"AAAA;;;;;;CAMC,GAED,SAASA,eAAe,QAA8B,sBAAsB;AAC5E,SAAkCC,iBAAiB,QAAQ,sBAAsB;AACjF,SAASC,4BAA4B,QAAQ,2BAA2B;AACxE,SAAwBC,gBAAgB,QAAQ,0BAA0B;AAC1E,SAASC,gBAAgB,QAAqC,qBAAqB;AACnF,SAAgCC,kBAAkB,QAAQ,6BAA6B;AACvF,SAASC,qBAAqB,QAAQ,gCAAgC;AACtE,0BAAyB,qBAAqB;AAA9C,SAAO,YAAKC,OAAO,GAA2B;AAC9C,SAASC,YAAY,EAAEC,WAAW,EAAEC,cAAc,QAA4B,oBAAoB;AAClG,cAAc,aAAa"}
|