@contentstack/cli-auth
Version:
Contentstack CLI plugin for authentication activities
134 lines (133 loc) • 6.06 kB
JavaScript
;
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();