From 4fe137426b2cdbc7b23999f1c1c41d4e40dcbad3 Mon Sep 17 00:00:00 2001 From: Basil Hosmer Date: Mon, 15 Sep 2025 02:11:07 -0400 Subject: [PATCH] Add debug logging for OAuth flow troubleshooting Add logging to track authorization flow steps and token introspection to help diagnose issues with separate mode authentication. --- src/auth/provider.ts | 7 +++++++ src/handlers/fakeauth.ts | 30 +++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/auth/provider.ts b/src/auth/provider.ts index 3d77c61..d522f2f 100644 --- a/src/auth/provider.ts +++ b/src/auth/provider.ts @@ -18,6 +18,7 @@ import { saveRefreshToken, } from '../services/auth.js'; import { InvalidTokenError } from '@modelcontextprotocol/sdk/server/auth/errors.js'; +import { logger } from '../utils/logger.js'; /** * Implementation of the OAuthRegisteredClientsStore interface using the existing client registration system @@ -67,6 +68,12 @@ export class EverythingAuthProvider implements OAuthServerProvider { state: params.state, }); + logger.debug('Saved pending authorization', { + authorizationCode: authorizationCode.substring(0, 8) + '...', + clientId: client.client_id, + state: params.state?.substring(0, 8) + '...' + }); + // TODO: should we use a different key, other than the authorization code, to store the pending authorization? // You can redirect to another page, or you can send an html response directly diff --git a/src/handlers/fakeauth.ts b/src/handlers/fakeauth.ts index 49f1f70..0ccc5d3 100644 --- a/src/handlers/fakeauth.ts +++ b/src/handlers/fakeauth.ts @@ -1,6 +1,7 @@ import { Request, Response } from "express"; import { generateMcpTokens, readPendingAuthorization, saveMcpInstallation, saveRefreshToken, saveTokenExchange } from "../services/auth.js"; import { McpInstallation } from "../types.js"; +import { logger } from "../utils/logger.js"; // this module has a fake upstream auth server that returns a fake auth code, it also allows you to authorize or fail // authorization, to test the different flows @@ -266,6 +267,12 @@ export async function handleFakeAuthorizeRedirect(req: Request, res: Response) { userId, // User ID from the authorization flow } = req.query; + logger.debug('Fake auth redirect received', { + mcpAuthorizationCode: typeof mcpAuthorizationCode === 'string' ? mcpAuthorizationCode.substring(0, 8) + '...' : mcpAuthorizationCode, + upstreamAuthorizationCode: typeof upstreamAuthorizationCode === 'string' ? upstreamAuthorizationCode.substring(0, 8) + '...' : upstreamAuthorizationCode, + userId + }); + // This is where you'd exchange the upstreamAuthorizationCode for access/refresh tokens // In this case, we're just going to fake it const upstreamTokens = await fakeUpstreamTokenExchange(upstreamAuthorizationCode as string); @@ -276,12 +283,21 @@ export async function handleFakeAuthorizeRedirect(req: Request, res: Response) { } const pendingAuth = await readPendingAuthorization(mcpAuthorizationCode); + logger.debug('Reading pending authorization', { + mcpAuthorizationCode: mcpAuthorizationCode.substring(0, 8) + '...', + found: !!pendingAuth + }); + if (!pendingAuth) { throw new Error("No matching authorization found"); } - + logger.debug('Generating MCP tokens'); const mcpTokens = generateMcpTokens(); + logger.debug('MCP tokens generated', { + hasAccessToken: !!mcpTokens.access_token, + hasRefreshToken: !!mcpTokens.refresh_token + }); const mcpInstallation: McpInstallation = { fakeUpstreamInstallation: { @@ -294,25 +310,37 @@ export async function handleFakeAuthorizeRedirect(req: Request, res: Response) { userId: (userId as string) || 'anonymous-user', // Include user ID from auth flow } + logger.debug('Saving MCP installation'); // Store the upstream authorization data await saveMcpInstallation(mcpTokens.access_token, mcpInstallation); + logger.debug('MCP installation saved'); // Store the refresh token -> access token mapping if (mcpTokens.refresh_token) { + logger.debug('Saving refresh token mapping'); await saveRefreshToken(mcpTokens.refresh_token, mcpTokens.access_token); + logger.debug('Refresh token mapping saved'); } + logger.debug('Saving token exchange data'); // Store the token exchange data await saveTokenExchange(mcpAuthorizationCode, { mcpAccessToken: mcpTokens.access_token, alreadyUsed: false, }); + logger.debug('Token exchange data saved'); // Redirect back to the original application with the authorization code and state const redirectUrl = pendingAuth.state ? `${pendingAuth.redirectUri}?code=${mcpAuthorizationCode}&state=${pendingAuth.state}` : `${pendingAuth.redirectUri}?code=${mcpAuthorizationCode}`; + + logger.debug('Redirecting to callback', { + redirectUrl, + hasState: !!pendingAuth.state + }); res.redirect(redirectUrl); + logger.debug('Redirect completed'); }; function fakeUpstreamTokenExchange(