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
// TODO: make an implementation of this using the ProxyAuthProvider
// This is mocking an upstream auth server. This wouldn't normally be in the same server as the MCP auth server
export async function handleFakeAuthorize(req: Request, res: Response) {
// get the redirect_uri and state from the query params
const { redirect_uri, state } = req.query;
// Set a more permissive CSP for auth pages to allow inline styles and scripts
res.setHeader('Content-Security-Policy', [
"default-src 'self'",
"style-src 'self' 'unsafe-inline'", // Allow inline styles for auth page styling
"script-src 'self' 'unsafe-inline'", // Allow inline scripts for auth page functionality
"object-src 'none'",
"frame-ancestors 'none'",
"form-action 'self'",
"base-uri 'self'"
].join('; '));
res.send(`
Upstream Provider Authentication
🔒
Upstream Authentication
Please verify your identity with the upstream provider
Your User Identity
Loading...
Testing Multiple Users: Open this page in different browser windows or incognito tabs to simulate different users. Each will have their own unique User ID and separate MCP sessions.
`);
}
// This is the callback URL that the upstream auth server will redirect to after authorization
export async function handleFakeAuthorizeRedirect(req: Request, res: Response) {
const {
// The state returned from the upstream auth server is actually the authorization code
state: mcpAuthorizationCode,
code: upstreamAuthorizationCode,
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);
// Validate that it's a string
if (typeof mcpAuthorizationCode !== "string") {
throw new Error("Invalid authorization code");
}
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: {
fakeAccessTokenForDemonstration: upstreamTokens.access_token,
fakeRefreshTokenForDemonstration: upstreamTokens.refresh_token,
},
mcpTokens,
clientId: pendingAuth.clientId,
issuedAt: Date.now() / 1000,
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(
authorizationCode: string,
): Promise<{ access_token: string; refresh_token: string }> {
// just return the authorization code with a suffix
return new Promise((resolve) => {
resolve({
access_token: `${authorizationCode}-exchanged-for-access-token`,
refresh_token: `${authorizationCode}-exchanged-for-refresh-token`,
});
});
}