doclyft
Version:
CLI for DocLyft - Interactive documentation generator with hosted documentation support
273 lines (272 loc) • 11.4 kB
JavaScript
;
/**
* 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;