auth-wizard.tsā¢8.17 kB
/**
* Authentication Setup Wizard
* Interactive wizard for setting up Google OAuth authentication
*/
import inquirer from 'inquirer';
import chalk from 'chalk';
import ora from 'ora';
import { ConfigManager } from '../config/config-manager.js';
import { AuthService } from '../auth/auth-service.js';
import { ServerConfig } from '../types/config.js';
export class AuthSetupWizard {
private configManager: ConfigManager;
constructor(configPath?: string) {
this.configManager = new ConfigManager(configPath);
}
/**
* Run the authentication setup wizard
*/
async run(): Promise<void> {
console.log(chalk.blue.bold('\nš Google Drive MCP Server - Authentication Setup\n'));
// Check if config exists
let config: ServerConfig;
try {
config = await this.configManager.loadConfig();
} catch (error) {
console.log(chalk.yellow('Configuration file not found. Creating default configuration...'));
await this.configManager.generateDefaultConfig();
config = await this.configManager.loadConfig();
}
// Check current authentication status
const spinner = ora('Checking current authentication status...').start();
let currentlyAuthenticated = false;
try {
const authService = new AuthService();
await authService.initialize();
const status = await authService.validateAuthentication();
currentlyAuthenticated = status.isValid;
spinner.succeed(currentlyAuthenticated ? 'Currently authenticated' : 'Not currently authenticated');
} catch (error) {
spinner.fail('Unable to check authentication status');
}
if (currentlyAuthenticated) {
const { proceed } = await inquirer.prompt([
{
type: 'confirm',
name: 'proceed',
message: 'You are already authenticated. Do you want to reconfigure?',
default: false
}
]);
if (!proceed) {
console.log(chalk.green('Authentication setup cancelled.'));
return;
}
}
// Step 1: Google OAuth Configuration
console.log(chalk.blue('\nš Step 1: Google OAuth Configuration'));
console.log(chalk.gray('You need to create a Google Cloud Project and OAuth 2.0 credentials.'));
console.log(chalk.gray('Visit: https://console.cloud.google.com/apis/credentials\n'));
const oauthAnswers = await inquirer.prompt([
{
type: 'input',
name: 'clientId',
message: 'Enter your Google OAuth Client ID:',
default: config.google.clientId,
validate: (input: string) => {
if (!input.trim()) {
return 'Client ID is required';
}
if (!input.includes('.googleusercontent.com')) {
return 'Client ID should end with .googleusercontent.com';
}
return true;
}
},
{
type: 'password',
name: 'clientSecret',
message: 'Enter your Google OAuth Client Secret:',
default: config.google.clientSecret,
validate: (input: string) => {
if (!input.trim()) {
return 'Client Secret is required';
}
return true;
}
},
{
type: 'input',
name: 'redirectUri',
message: 'Enter your OAuth Redirect URI:',
default: config.google.redirectUri,
validate: (input: string) => {
if (!input.trim()) {
return 'Redirect URI is required';
}
try {
new URL(input);
return true;
} catch {
return 'Please enter a valid URL';
}
}
}
]);
// Update configuration with OAuth settings
const updatedConfig: Partial<ServerConfig> = {
google: {
...config.google,
clientId: oauthAnswers.clientId,
clientSecret: oauthAnswers.clientSecret,
redirectUri: oauthAnswers.redirectUri
}
};
await this.configManager.updateConfig(updatedConfig);
console.log(chalk.green('ā OAuth configuration saved'));
// Step 2: Authentication Flow
console.log(chalk.blue('\nš Step 2: Authentication Flow'));
const authService = new AuthService();
await authService.initialize();
// Get authorization URL
await authService.performInitialSetup();
console.log(chalk.yellow('\nPlease complete the following steps:'));
console.log(chalk.yellow('1. Open the URL above in your browser'));
console.log(chalk.yellow('2. Sign in to your Google account'));
console.log(chalk.yellow('3. Grant the requested permissions'));
console.log(chalk.yellow('4. Copy the authorization code from the redirect URL\n'));
const { authCode } = await inquirer.prompt([
{
type: 'input',
name: 'authCode',
message: 'Enter the authorization code:',
validate: (input: string) => {
if (!input.trim()) {
return 'Authorization code is required';
}
return true;
}
}
]);
// Exchange code for tokens
const exchangeSpinner = ora('Exchanging authorization code for tokens...').start();
try {
const success = await authService.exchangeCodeForTokens(authCode.trim());
if (success) {
exchangeSpinner.succeed('Authentication successful!');
// Test the authentication
const testSpinner = ora('Testing authentication...').start();
const status = await authService.validateAuthentication();
if (status.isValid) {
testSpinner.succeed('Authentication test passed');
console.log(chalk.green.bold('\nš Authentication setup completed successfully!'));
console.log(chalk.blue('You can now start the MCP server with: google-drive-mcp start'));
} else {
testSpinner.fail('Authentication test failed');
console.log(chalk.red(`Error: ${status.error}`));
}
} else {
exchangeSpinner.fail('Failed to exchange authorization code');
console.log(chalk.red('Please check your authorization code and try again.'));
}
} catch (error) {
exchangeSpinner.fail('Authentication failed');
console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
}
// Optional: Additional configuration
const { configureAdvanced } = await inquirer.prompt([
{
type: 'confirm',
name: 'configureAdvanced',
message: 'Would you like to configure advanced settings?',
default: false
}
]);
if (configureAdvanced) {
await this.configureAdvancedSettings();
}
}
/**
* Configure advanced settings
*/
private async configureAdvancedSettings(): Promise<void> {
console.log(chalk.blue('\nāļø Advanced Configuration'));
const config = await this.configManager.loadConfig();
const advancedAnswers = await inquirer.prompt([
{
type: 'input',
name: 'cacheDirectory',
message: 'Cache directory:',
default: config.cache.directory
},
{
type: 'input',
name: 'maxFileSize',
message: 'Maximum file size to process:',
default: config.processing.maxFileSize
},
{
type: 'list',
name: 'logLevel',
message: 'Log level:',
choices: ['debug', 'info', 'warn', 'error'],
default: config.server.logLevel
},
{
type: 'number',
name: 'cacheTTL',
message: 'Cache TTL (seconds):',
default: config.cache.defaultTTL
}
]);
const updatedConfig: Partial<ServerConfig> = {
cache: {
...config.cache,
directory: advancedAnswers.cacheDirectory,
defaultTTL: advancedAnswers.cacheTTL
},
processing: {
...config.processing,
maxFileSize: advancedAnswers.maxFileSize
},
server: {
...config.server,
logLevel: advancedAnswers.logLevel
}
};
await this.configManager.updateConfig(updatedConfig);
console.log(chalk.green('ā Advanced configuration saved'));
}
}