UNPKG

@contentstack/cli-auth

Version:

Contentstack CLI plugin for authentication activities

134 lines (133 loc) 6.06 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const cli_utilities_1 = require("@contentstack/cli-utilities"); const otplib_1 = require("otplib"); const interactive_1 = require("./interactive"); /** * @class * MFA handler for managing multi-factor authentication */ class MFAHandler { constructor() { this.encrypter = new cli_utilities_1.NodeCrypto(); } /** * Validates if a string is a valid base32 secret * @param secret The secret to validate * @returns true if valid, false otherwise */ isValidBase32(secret) { // Base32 string must: // 1. Contain only uppercase letters A-Z and digits 2-7 // 2. Be at least 16 characters long (before padding) // 3. Have valid padding (no single = character) const base32Regex = /^[A-Z2-7]+(?:={2,6})?$/; const nonPaddedLength = secret.replace(/=+$/, '').length; return base32Regex.test(secret) && nonPaddedLength >= 16; } /** * Generates an MFA code from a provided secret * @param secret The MFA secret to use * @returns string The generated MFA code * @throws Error if the secret is invalid or code generation fails */ generateMFACode(secret) { cli_utilities_1.log.debug('Generating MFA code from provided secret', { module: 'mfa-handler' }); try { // Validate and normalize secret const normalizedSecret = secret.toUpperCase(); if (!this.isValidBase32(normalizedSecret)) { cli_utilities_1.log.debug('Invalid MFA secret format', { module: 'mfa-handler' }); cli_utilities_1.cliux.print('CLI_AUTH_MFA_INVALID_SECRET', { color: 'yellow' }); cli_utilities_1.cliux.print('For more information about MFA, visit: https://www.contentstack.com/docs/developers/security/multi-factor-authentication', { color: 'yellow' }); process.exit(1); } // Generate MFA code const code = otplib_1.authenticator.generate(normalizedSecret); cli_utilities_1.log.debug('Generated MFA code successfully', { module: 'mfa-handler' }); return code; } catch (error) { cli_utilities_1.log.debug('Failed to generate MFA code', { module: 'mfa-handler', error }); cli_utilities_1.cliux.print('CLI_AUTH_MFA_GENERATION_FAILED', { color: 'yellow' }); throw error; } } /** * Gets MFA code from stored configuration * @returns Promise<string> The MFA code * @throws Error if MFA code generation fails */ async getMFACode() { cli_utilities_1.log.debug('Getting MFA code', { module: 'mfa-handler' }); let secret; let source; const envSecret = process.env.CONTENTSTACK_MFA_SECRET; if (envSecret) { cli_utilities_1.log.debug('Found MFA secret in environment variable', { module: 'mfa-handler' }); if (!this.isValidBase32(envSecret.toUpperCase())) { cli_utilities_1.log.debug('Invalid MFA secret format from environment variable', { module: 'mfa-handler' }); cli_utilities_1.cliux.print('CLI_AUTH_MFA_INVALID_SECRET', { color: 'yellow' }); cli_utilities_1.cliux.print('For more information about MFA, visit: https://www.contentstack.com/docs/developers/security/multi-factor-authentication', { color: 'yellow' }); } else { secret = envSecret; source = 'environment variable'; } } if (!secret) { cli_utilities_1.log.debug('Checking stored MFA secret', { module: 'mfa-handler' }); const mfaConfig = cli_utilities_1.configHandler.get('mfa'); if (mfaConfig === null || mfaConfig === void 0 ? void 0 : mfaConfig.secret) { try { secret = this.encrypter.decrypt(mfaConfig.secret); source = 'stored configuration'; } catch (error) { cli_utilities_1.log.debug('Failed to decrypt stored MFA secret', { module: 'mfa-handler', error }); (0, cli_utilities_1.handleAndLogError)(error, { module: 'mfa-handler' }, cli_utilities_1.messageHandler.parse('CLI_AUTH_MFA_DECRYPT_FAILED')); } } } if (secret) { try { const code = this.generateMFACode(secret); cli_utilities_1.log.debug('Generated MFA code', { module: 'mfa-handler', source }); return code; } catch (error) { cli_utilities_1.log.debug('Failed to generate MFA code', { module: 'mfa-handler', error, source }); cli_utilities_1.cliux.print('CLI_AUTH_MFA_GENERATION_FAILED', { color: 'yellow' }); cli_utilities_1.cliux.print('CLI_AUTH_MFA_RECONFIGURE_HINT'); cli_utilities_1.cliux.print('For more information about MFA, visit: https://www.contentstack.com/docs/developers/security/multi-factor-authentication', { color: 'yellow' }); } } } /** * Gets MFA code through manual user input * @returns Promise<string> The MFA code * @throws Error if code format is invalid */ async getManualMFACode() { try { const code = await (0, interactive_1.askOTP)(); if (!/^\d{6}$/.test(code)) { throw new Error(cli_utilities_1.messageHandler.parse('CLI_AUTH_MFA_INVALID_CODE')); } return code; } catch (error) { cli_utilities_1.log.debug('Failed to get MFA code', { module: 'mfa-handler', error }); throw error; } } /** * Validates an MFA code format * @param code The MFA code to validate * @returns boolean True if valid, false otherwise */ isValidMFACode(code) { return /^\d{6}$/.test(code); } } exports.default = new MFAHandler();