UNPKG

doclyft

Version:

CLI for DocLyft - Interactive documentation generator with hosted documentation support

273 lines (272 loc) 11.4 kB
"use strict"; /** * Authentication middleware for CLI commands * Provides centralized authentication validation and session management */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AuthMiddleware = exports.AUTH_ERRORS = exports.AuthError = void 0; const config_1 = __importDefault(require("../services/config")); const api_1 = __importDefault(require("../services/api")); const chalk_1 = __importDefault(require("chalk")); class AuthError extends Error { constructor(message, code) { super(message); this.code = code; this.name = 'AuthError'; } } exports.AuthError = AuthError; exports.AUTH_ERRORS = { NOT_LOGGED_IN: 'Not authenticated. Please run `doclyft login` first.', INVALID_TOKEN: 'Authentication token is invalid. Please run `doclyft login` again.', SESSION_EXPIRED: 'Session expired. Please run `doclyft login` again.', NETWORK_ERROR: 'Cannot verify authentication. Check your connection and try again.', MISSING_USER_INFO: 'User information not found. Please run `doclyft login` again.', TOKEN_FORMAT_INVALID: 'Stored token has invalid format. Please run `doclyft login` again.' }; class AuthMiddleware { /** * Main authentication check for protected commands * Throws AuthError if authentication fails */ static async requireAuth() { try { // Check if basic auth data exists const token = config_1.default.get('token'); const user_id = config_1.default.get('user_id'); const user_email = config_1.default.get('user_email'); if (!token) { throw new AuthError(exports.AUTH_ERRORS.NOT_LOGGED_IN, 'NO_TOKEN'); } if (!user_id || !user_email) { throw new AuthError(exports.AUTH_ERRORS.MISSING_USER_INFO, 'NO_USER_INFO'); } // Validate token format if (!this.isValidTokenFormat(token)) { throw new AuthError(exports.AUTH_ERRORS.TOKEN_FORMAT_INVALID, 'INVALID_FORMAT'); } // Check session validity if SessionManager is available try { // Dynamic import to avoid circular dependencies const { default: SessionManager } = await Promise.resolve().then(() => __importStar(require('../services/session'))); const isSessionValid = await SessionManager.validateSession(); if (!isSessionValid) { throw new AuthError(exports.AUTH_ERRORS.SESSION_EXPIRED, 'SESSION_EXPIRED'); } } catch (error) { // If session manager is not available or throws an error, // fall back to simple token validation if (error instanceof AuthError) { throw error; } // Test token connectivity (with timeout) const isValid = await this.validateTokenWithTimeout(token); if (!isValid) { throw new AuthError(exports.AUTH_ERRORS.INVALID_TOKEN, 'TOKEN_INVALID'); } } return { isAuthenticated: true, user_id, user_email, token }; } catch (error) { if (error instanceof AuthError) { throw error; } // Network or other errors throw new AuthError(exports.AUTH_ERRORS.NETWORK_ERROR, 'NETWORK_FAILURE'); } } /** * Quick authentication check without network validation * Used for commands that need basic auth info but can work offline */ static async quickAuthCheck() { const token = config_1.default.get('token'); const user_id = config_1.default.get('user_id'); const user_email = config_1.default.get('user_email'); if (!token || !user_id || !user_email) { return { isAuthenticated: false, error: exports.AUTH_ERRORS.NOT_LOGGED_IN }; } if (!this.isValidTokenFormat(token)) { return { isAuthenticated: false, error: exports.AUTH_ERRORS.TOKEN_FORMAT_INVALID }; } return { isAuthenticated: true, user_id, user_email, token }; } /** * Validate token format without network call */ static isValidTokenFormat(token) { // DocLyft API tokens should be alphanumeric with specific patterns // Add validation based on your token format if (!token || typeof token !== 'string') { return false; } // Trim the token to handle any whitespace issues const cleanToken = token.trim(); // Basic validation - tokens should be reasonable length and format if (cleanToken.length < 10 || cleanToken.length > 500) { return false; } // Check for DocLyft API token prefix if (!cleanToken.startsWith('dk_prod_')) { return false; } // Should not contain obvious invalid characters if (cleanToken.includes(' ') || cleanToken.includes('\n') || cleanToken.includes('\t')) { return false; } return true; } /** * Validate token with API call and timeout */ static async validateTokenWithTimeout(token, timeout = 5000) { return new Promise((resolve) => { const timeoutId = setTimeout(() => { resolve(false); }, timeout); api_1.default.verifyToken(token) .then((isValid) => { clearTimeout(timeoutId); resolve(isValid); }) .catch(() => { clearTimeout(timeoutId); resolve(false); }); }); } /** * Get user authentication info if available */ static getAuthInfo() { return { user_id: config_1.default.get('user_id'), user_email: config_1.default.get('user_email'), token: config_1.default.get('token') }; } /** * Clear all authentication data */ static clearAuth() { config_1.default.delete('token'); config_1.default.delete('user_id'); config_1.default.delete('user_email'); config_1.default.delete('github_token'); } /** * Format authentication error for display */ static formatAuthError(error, context) { const contextMsg = context ? ` during ${context}` : ''; switch (error.code) { case 'NO_TOKEN': return chalk_1.default.red(`❌ Not authenticated${contextMsg}`) + '\n' + chalk_1.default.blue(' Run: ') + chalk_1.default.cyan('doclyft login'); case 'NO_USER_INFO': return chalk_1.default.red(`❌ User information missing${contextMsg}`) + '\n' + chalk_1.default.blue(' Run: ') + chalk_1.default.cyan('doclyft login'); case 'INVALID_FORMAT': return chalk_1.default.red(`❌ Invalid token format${contextMsg}`) + '\n' + chalk_1.default.blue(' Run: ') + chalk_1.default.cyan('doclyft login'); case 'TOKEN_INVALID': return chalk_1.default.red(`❌ Authentication token expired or invalid${contextMsg}`) + '\n' + chalk_1.default.blue(' Run: ') + chalk_1.default.cyan('doclyft login'); case 'NETWORK_FAILURE': return chalk_1.default.red(`❌ Cannot verify authentication${contextMsg}`) + '\n' + chalk_1.default.yellow(' Check your internet connection and try again'); default: return chalk_1.default.red(`❌ Authentication failed${contextMsg}`) + '\n' + chalk_1.default.blue(' Run: ') + chalk_1.default.cyan('doclyft login'); } } /** * Show helpful authentication guidance */ static showAuthGuidance() { console.log(chalk_1.default.blue('\n🔐 Authentication Required')); console.log(chalk_1.default.blue('To use DocLyft CLI, you need to authenticate first:\n')); console.log(chalk_1.default.cyan('1. Get your API key:')); console.log(chalk_1.default.gray(' • Visit: https://doclyft.com/dashboard/api-keys')); console.log(chalk_1.default.gray(' • Generate a new API key')); console.log(chalk_1.default.gray(' • Copy the key\n')); console.log(chalk_1.default.cyan('2. Login to CLI:')); console.log(chalk_1.default.gray(' • Run: ') + chalk_1.default.white('doclyft login')); console.log(chalk_1.default.gray(' • Paste your API key when prompted\n')); console.log(chalk_1.default.cyan('3. Verify authentication:')); console.log(chalk_1.default.gray(' • Run: ') + chalk_1.default.white('doclyft status')); console.log(chalk_1.default.gray(' • Should show "✅ Authenticated with DocLyft"\n')); console.log(chalk_1.default.blue('💡 Need help? Visit: https://docs.doclyft.com/cli/authentication')); } /** * Check if a command requires authentication */ static requiresAuth(commandName) { const publicCommands = [ 'help', 'version', '--help', '--version', 'login', 'logout' ]; // Extract main command (handle subcommands) const mainCommand = commandName.split(' ')[0]; return !publicCommands.includes(mainCommand) && !publicCommands.includes(commandName); } } exports.AuthMiddleware = AuthMiddleware; exports.default = AuthMiddleware;