#!/usr/bin/env node
/**
* OAuth Flow Script
*
* Run this script to authenticate with Google APIs using OAuth.
* Usage: pnpm auth:oauth
*/
import * as http from 'http';
import * as url from 'url';
import { getAuthUrl, exchangeCode } from './google-auth.js';
const PORT = 3000;
const REDIRECT_URI = `http://localhost:${PORT}`;
async function runOAuthFlow(): Promise<void> {
// Generate auth URL with our redirect URI
const authUrl = getAuthUrl(REDIRECT_URI);
// eslint-disable-next-line no-console
console.log('\n========================================');
// eslint-disable-next-line no-console
console.log('Google OAuth Authentication');
// eslint-disable-next-line no-console
console.log('========================================\n');
// eslint-disable-next-line no-console
console.log('1. Open this URL in your browser:\n');
// eslint-disable-next-line no-console
console.log(` ${authUrl}\n`);
// eslint-disable-next-line no-console
console.log('2. Sign in with your Google account');
// eslint-disable-next-line no-console
console.log('3. Grant the requested permissions');
// eslint-disable-next-line no-console
console.log('4. Wait for the callback...\n');
// Start local server to receive callback
return new Promise((resolve, reject) => {
const server = http.createServer(async (req, res) => {
try {
const parsedUrl = url.parse(req.url || '', true);
// Handle callback at root path (Google redirects to http://localhost:PORT?code=xxx)
if (parsedUrl.pathname === '/' || parsedUrl.pathname === '/oauth/callback') {
const code = parsedUrl.query.code as string;
const error = parsedUrl.query.error as string;
if (error) {
res.writeHead(400, { 'Content-Type': 'text/html' });
res.end(`
<html>
<head><title>Authentication Failed</title></head>
<body>
<h1>Authentication Failed</h1>
<p>Error: ${error}</p>
<p>You can close this window.</p>
</body>
</html>
`);
server.close();
reject(new Error(`OAuth error: ${error}`));
return;
}
if (!code) {
res.writeHead(400, { 'Content-Type': 'text/html' });
res.end(`
<html>
<head><title>Authentication Failed</title></head>
<body>
<h1>Authentication Failed</h1>
<p>No authorization code received.</p>
<p>You can close this window.</p>
</body>
</html>
`);
server.close();
reject(new Error('No authorization code received'));
return;
}
// Exchange code for tokens
try {
await exchangeCode(code, REDIRECT_URI);
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(`
<html>
<head><title>Authentication Successful</title></head>
<body>
<h1>Authentication Successful!</h1>
<p>You can close this window and return to the terminal.</p>
</body>
</html>
`);
// eslint-disable-next-line no-console
console.log('\n========================================');
// eslint-disable-next-line no-console
console.log('Authentication Successful!');
// eslint-disable-next-line no-console
console.log('========================================');
// eslint-disable-next-line no-console
console.log('\nToken saved. You can now use the MCP server.');
// eslint-disable-next-line no-console
console.log('Run `pnpm dev` to start the server.\n');
server.close();
resolve();
} catch (exchangeError) {
res.writeHead(500, { 'Content-Type': 'text/html' });
res.end(`
<html>
<head><title>Token Exchange Failed</title></head>
<body>
<h1>Token Exchange Failed</h1>
<p>Error: ${exchangeError instanceof Error ? exchangeError.message : 'Unknown error'}</p>
<p>You can close this window.</p>
</body>
</html>
`);
server.close();
reject(exchangeError);
}
} else {
res.writeHead(404);
res.end('Not found');
}
} catch (err) {
console.error('Server error:', err);
res.writeHead(500);
res.end('Internal server error');
}
});
server.listen(PORT, () => {
// eslint-disable-next-line no-console
console.log(`Waiting for OAuth callback on http://localhost:${PORT}/oauth/callback ...`);
});
server.on('error', (err) => {
console.error('Server error:', err);
reject(err);
});
// Timeout after 5 minutes
setTimeout(() => {
server.close();
reject(new Error('OAuth flow timed out'));
}, 5 * 60 * 1000);
});
}
// Run if executed directly
runOAuthFlow().catch((err) => {
console.error('OAuth flow failed:', err);
process.exit(1);
});