import {Express, Request, Response} from 'express';
import {mcpAuthRouter} from '@modelcontextprotocol/sdk/server/auth/router.js';
import {GmailOAuthServerProvider} from './gmail/oauth-provider.js';
import {getRedirectUrl, InvalidStateError} from './oauth-bridge.js';
import {logger} from '../logger.js';
import {auditOauthFailure} from '../audit.js';
function sendAuthError(res: Response, message: string): void {
res.status(400).send(`
<html>
<body>
<h1>Authentication Failed</h1>
<p>${message}</p>
</body>
</html>
`);
}
export async function handleCallback(
req: Request,
res: Response,
provider: GmailOAuthServerProvider
): Promise<void> {
try {
const code = req.query.code as string;
const state = req.query.state as string;
if (!code || !state) {
auditOauthFailure({reason: 'missing_params'});
sendAuthError(res, 'Missing authorization code or state parameter.');
return;
}
const redirectUrl = await getRedirectUrl(provider, code, state);
res.redirect(redirectUrl);
} catch (error) {
if (error instanceof InvalidStateError) {
auditOauthFailure({reason: 'invalid_state'});
sendAuthError(res, error.message);
return;
}
auditOauthFailure({reason: error instanceof Error ? error.message : 'unknown'});
logger.error({err: error}, 'OAuth callback error');
sendAuthError(res, 'Failed to complete authentication. Please try again.');
}
}
export function registerAuthRoutes(
app: Express,
provider: GmailOAuthServerProvider,
issuerUrl: URL
): void {
const resourceServerUrl = new URL('/mcp', issuerUrl);
app.use(
mcpAuthRouter({
provider,
issuerUrl,
resourceServerUrl,
scopesSupported: ['gmail.readonly'],
})
);
app.get('/callback', (req, res) => handleCallback(req, res, provider));
}