Skip to content

Commit 8933eb6

Browse files
committed
Updated the config to remove the MFA Error handler and updated the login flow in the auth command
1 parent 10fb8dd commit 8933eb6

11 files changed

Lines changed: 125 additions & 132 deletions

File tree

.talismanrc

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,12 @@ fileignoreconfig:
6969
checksum: c300f5c7b5ebe755ef55765331446e619c9efd6f6f18d89a31017594a39acbe6
7070
- filename: packages/contentstack-auth/test/unit/commands/login.test.ts
7171
checksum: e8a1e413008e19de3c35cbf85d0d8433f0ac25ddf89d9a5cdd118bbc875321b9
72-
version: "1.0"
72+
- filename: packages/contentstack-auth/src/commands/auth/login.ts
73+
checksum: 2725fcbfd69f9290182073a362b0c2fa7de625fe6d2e0b0aa732d42678966179
74+
- filename: packages/contentstack-auth/src/utils/mfa-handler.ts
75+
checksum: a61790bd55c565cd56e5ad146816ab0a760f7ccc90c565cf7dff66a97ccef6e1
76+
- filename: packages/contentstack-config/test/unit/commands/mfa.test.ts
77+
checksum: 242491d40c7178132dc60abf3a251254559a002a9377b3038bc4a8b0c4708565
78+
version: ""
79+
80+

packages/contentstack-auth/messages/index.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@
5050
"CLI_AUTH_EXIT_PROCESS": "Exiting the process...",
5151
"CLI_SELECT_TOKEN_TYPE": "Select the type of token to add",
5252
"CLI_AUTH_MFA_INVALID_SECRET": "Invalid MFA secret format. Please check your authentication setup.",
53-
"CLI_AUTH_MFA_GENERATION_FAILED": "Failed to generate MFA code. Please try again.",
54-
"CLI_AUTH_MFA_DECRYPT_FAILED": "Failed to decrypt stored MFA secret. Please try again.",
53+
"CLI_AUTH_MFA_GENERATION_FAILED": "Failed to generate MFA code. Proceeding for Manual MFA code input.",
54+
"CLI_AUTH_MFA_DECRYPT_FAILED": "Failed to decrypt stored MFA secret. Try Resetting the MFA secret. Proceeding for Manual MFA code input.",
5555
"CLI_AUTH_MFA_INVALID_CODE": "Invalid authentication code format. Please enter a 6-digit code.",
5656
"CLI_AUTH_MFA_RECONFIGURE_HINT": "Consider reconfiguring MFA using config:mfa:add command.",
5757
"CLI_AUTH_SMS_OTP_FAILED": "Failed to send SMS OTP. Please try again or use a different 2FA method.",

packages/contentstack-auth/src/commands/auth/login.ts

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@ export default class LoginCommand extends BaseCommand<typeof LoginCommand> {
5858
log.debug('Initializing management API client', this.contextDetails);
5959
const managementAPIClient = await managementSDKClient({ host: this.cmaHost, skipTokenValidity: true });
6060
log.debug('Management API client initialized successfully', this.contextDetails);
61-
61+
6262
const { flags: loginFlags } = await this.parse(LoginCommand);
63-
log.debug('Token add flags parsed', {...this.contextDetails, flags: loginFlags});
63+
log.debug('Token add flags parsed', { ...this.contextDetails, flags: loginFlags });
6464

6565
authHandler.client = managementAPIClient;
6666
log.debug('Auth handler client set', this.contextDetails);
@@ -77,17 +77,22 @@ export default class LoginCommand extends BaseCommand<typeof LoginCommand> {
7777
log.debug('Starting basic authentication flow', this.contextDetails);
7878
const username = loginFlags?.username || (await interactive.askUsername());
7979
const password = loginFlags?.password || (await interactive.askPassword());
80-
log.debug('Credentials obtained', {
81-
...this.contextDetails,
82-
hasUsername: !!username,
83-
hasPassword: !!password
80+
log.debug('Credentials obtained', {
81+
...this.contextDetails,
82+
hasUsername: !!username,
83+
hasPassword: !!password,
8484
});
8585

8686
await this.login(username, password);
8787
}
8888
} catch (error) {
89-
log.debug('Login command failed', { ...this.contextDetails, error });
90-
cliux.error('CLI_AUTH_LOGIN_FAILED');
89+
log.debug('Login command failed', {
90+
...this.contextDetails,
91+
error,
92+
});
93+
if ((error.message && error.message.includes('2FA')) || error.message.includes('MFA')) {
94+
error.message = `${error.message}\nFor more information about MFA, visit: https://www.contentstack.com/docs/developers/security/multi-factor-authentication`;
95+
}
9196
handleAndLogError(error, { ...this.contextDetails });
9297
process.exit();
9398
}
@@ -99,10 +104,12 @@ export default class LoginCommand extends BaseCommand<typeof LoginCommand> {
99104
try {
100105
log.debug('Calling auth handler login', this.contextDetails);
101106
let tfaToken: string | undefined;
102-
107+
103108
try {
104109
tfaToken = await mfaHandler.getMFACode();
105-
log.debug('MFA token generated from stored configuration', this.contextDetails);
110+
if(tfaToken){
111+
log.debug('MFA token generated from stored configuration', this.contextDetails);
112+
}
106113
} catch (error) {
107114
log.debug('Failed to generate MFA token from config', { ...this.contextDetails, error });
108115
tfaToken = undefined;

packages/contentstack-auth/src/utils/auth-handler.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,18 +48,18 @@ class AuthHandler {
4848
try {
4949
await this.requestSMSOTP(loginPayload);
5050
} catch (error) {
51-
log.error('SMS OTP request failed', { module: 'auth-handler', error });
51+
log.debug('SMS OTP request failed', { module: 'auth-handler', error });
5252
cliux.print('CLI_AUTH_SMS_OTP_FAILED', { color: 'yellow' });
53-
handleAndLogError(error, { module: 'auth-handler' });
53+
throw error;
5454
}
5555
}
5656

5757
log.debug('Requesting OTP input', { module: 'auth-handler', channel: otpChannel });
5858
return await askOTP();
5959
} catch (error) {
60-
log.error('2FA flow failed', { module: 'auth-handler', error });
60+
log.debug('2FA flow failed', { module: 'auth-handler', error });
6161
cliux.print('CLI_AUTH_2FA_FAILED', { color: 'yellow' });
62-
handleAndLogError(error, { module: 'auth-handler' });
62+
throw error;
6363
}
6464
}
6565

@@ -75,8 +75,7 @@ class AuthHandler {
7575
log.debug('SMS OTP request successful', { module: 'auth-handler' });
7676
cliux.print('CLI_AUTH_LOGIN_SECURITY_CODE_SEND_SUCCESS');
7777
} catch (error) {
78-
log.error('SMS OTP request failed', { module: 'auth-handler', error });
79-
handleAndLogError(error, { module: 'auth-handler' });
78+
log.debug('SMS OTP request failed', { module: 'auth-handler', error });
8079
throw error;
8180
}
8281
}
@@ -136,7 +135,6 @@ class AuthHandler {
136135
return;
137136
}
138137
} else {
139-
log.debug('Login failed - no user found', { module: 'auth-handler', result });
140138
log.debug('Login failed - no user found', { module: 'auth-handler', result });
141139
cliux.print('CLI_AUTH_LOGIN_NO_USER', { color: 'yellow' });
142140
handleAndLogError(new Error(messageHandler.parse('CLI_AUTH_LOGIN_NO_USER')), { module: 'auth-handler' });

packages/contentstack-auth/src/utils/mfa-handler.ts

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,18 @@ class MFAHandler {
3636
*/
3737
generateMFACode(secret: string): string {
3838
log.debug('Generating MFA code from provided secret', { module: 'mfa-handler' });
39-
39+
4040
try {
4141
// Validate and normalize secret
4242
const normalizedSecret = secret.toUpperCase();
4343
if (!this.isValidBase32(normalizedSecret)) {
4444
log.debug('Invalid MFA secret format', { module: 'mfa-handler' });
4545
cliux.print('CLI_AUTH_MFA_INVALID_SECRET', { color: 'yellow' });
46-
throw new Error(messageHandler.parse('CLI_AUTH_MFA_INVALID_SECRET'));
46+
cliux.print(
47+
'For more information about MFA, visit: https://www.contentstack.com/docs/developers/security/multi-factor-authentication',
48+
{ color: 'yellow' },
49+
);
50+
process.exit(1);
4751
}
4852

4953
// Generate MFA code
@@ -70,8 +74,17 @@ class MFAHandler {
7074
const envSecret = process.env.CONTENTSTACK_MFA_SECRET;
7175
if (envSecret) {
7276
log.debug('Found MFA secret in environment variable', { module: 'mfa-handler' });
73-
secret = envSecret;
74-
source = 'environment variable';
77+
if (!this.isValidBase32(envSecret.toUpperCase())) {
78+
log.debug('Invalid MFA secret format from environment variable', { module: 'mfa-handler' });
79+
cliux.print('CLI_AUTH_MFA_INVALID_SECRET', { color: 'yellow' });
80+
cliux.print(
81+
'For more information about MFA, visit: https://www.contentstack.com/docs/developers/security/multi-factor-authentication',
82+
{ color: 'yellow' },
83+
);
84+
} else {
85+
secret = envSecret;
86+
source = 'environment variable';
87+
}
7588
}
7689

7790
if (!secret) {
@@ -98,13 +111,12 @@ class MFAHandler {
98111
log.debug('Failed to generate MFA code', { module: 'mfa-handler', error, source });
99112
cliux.print('CLI_AUTH_MFA_GENERATION_FAILED', { color: 'yellow' });
100113
cliux.print('CLI_AUTH_MFA_RECONFIGURE_HINT');
101-
handleAndLogError(new Error(messageHandler.parse('CLI_AUTH_MFA_GENERATION_FAILED')), { module: 'mfa-handler' });
114+
cliux.print(
115+
'For more information about MFA, visit: https://www.contentstack.com/docs/developers/security/multi-factor-authentication',
116+
{ color: 'yellow' },
117+
);
102118
}
103119
}
104-
105-
// No secret available, ask for manual input
106-
log.debug('No MFA secret found, requesting manual input', { module: 'mfa-handler' });
107-
return this.getManualMFACode();
108120
}
109121

110122
/**
@@ -147,4 +159,3 @@ class MFAHandler {
147159
}
148160

149161
export default new MFAHandler();
150-

packages/contentstack-config/src/commands/config/mfa/add.ts

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import { cliux } from '@contentstack/cli-utilities';
1+
import { cliux, handleAndLogError } from '@contentstack/cli-utilities';
22
import { BaseCommand } from '../../../base-command';
33
import { MFAService } from '../../../services/mfa/mfa.service';
4-
import { MFAError } from '../../../services/mfa/mfa.types';
54

65
export default class AddMFACommand extends BaseCommand<typeof AddMFACommand> {
76
static readonly description = 'Add MFA secret for 2FA authentication';
@@ -25,7 +24,9 @@ export default class AddMFACommand extends BaseCommand<typeof AddMFACommand> {
2524
const envSecret = process.env.CONTENTSTACK_MFA_SECRET;
2625
if (envSecret) {
2726
if (!this.mfaService.validateSecret(envSecret)) {
28-
throw new MFAError('Invalid secret format in environment variable');
27+
cliux.error('Invalid secret format in environment variable. The secret must be a valid Base32 string of at least 16 characters.');
28+
cliux.print('For more information about MFA, visit: https://www.contentstack.com/docs/developers/security/multi-factor-authentication', { color: 'yellow' });
29+
process.exit(1);
2930
}
3031
cliux.print('Using MFA secret from environment variable');
3132
return;
@@ -42,15 +43,18 @@ export default class AddMFACommand extends BaseCommand<typeof AddMFACommand> {
4243
process.exit(1);
4344
}
4445
if (!this.mfaService.validateSecret(input)) {
45-
cliux.error('Invalid secret format');
46+
cliux.error('Invalid secret format. The secret must be a valid Base32 string of at least 16 characters.');
47+
cliux.print('For more information about MFA, visit: https://www.contentstack.com/docs/developers/security/multi-factor-authentication', { color: 'yellow' });
4648
process.exit(1);
4749
}
4850
return true;
4951
},
5052
});
5153

5254
if (!secret || !this.mfaService.validateSecret(secret)) {
53-
throw new MFAError('Invalid secret format');
55+
cliux.error('Invalid secret format. The secret must be a valid Base32 string of at least 16 characters.');
56+
cliux.print('For more information about MFA, visit: https://www.contentstack.com/docs/developers/security/multi-factor-authentication', { color: 'yellow' });
57+
process.exit(1);
5458
}
5559

5660
// Check if MFA configuration already exists
@@ -74,18 +78,12 @@ export default class AddMFACommand extends BaseCommand<typeof AddMFACommand> {
7478
this.mfaService.storeConfig({ secret: encryptedSecret });
7579
cliux.success('Secret has been stored successfully');
7680
} catch (error) {
77-
if (error instanceof MFAError) {
78-
throw error;
79-
}
80-
throw new MFAError('Failed to store secret');
81+
handleAndLogError(error, { module: 'config:mfa:add' });
82+
process.exit(1);
8183
}
8284
} catch (error) {
83-
if (error instanceof MFAError) {
84-
cliux.error(error.message);
85-
} else {
86-
cliux.error('Failed to store secret');
87-
}
88-
throw error;
85+
handleAndLogError(error, { module: 'config:mfa:add' });
86+
process.exit(1);
8987
}
9088
}
9189
}

packages/contentstack-config/src/commands/config/mfa/remove.ts

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import { cliux } from '@contentstack/cli-utilities';
1+
import { cliux, handleAndLogError } from '@contentstack/cli-utilities';
22
import { BaseCommand } from '../../../base-command';
33
import { MFAService } from '../../../services/mfa/mfa.service';
4-
import { MFAError } from '../../../services/mfa/mfa.types';
54
import { Flags } from '@oclif/core';
65

76
export default class RemoveMFACommand extends BaseCommand<typeof RemoveMFACommand> {
@@ -32,16 +31,10 @@ export default class RemoveMFACommand extends BaseCommand<typeof RemoveMFAComman
3231
const { flags } = await this.parse(RemoveMFACommand);
3332

3433
let config;
35-
try {
36-
config = this.mfaService.getStoredConfig();
37-
if (!config?.secret) {
38-
throw new MFAError('Failed to remove secret configuration');
39-
}
40-
} catch (error) {
41-
if (error instanceof MFAError) {
42-
throw error;
43-
}
44-
throw new MFAError('Failed to remove secret configuration');
34+
config = this.mfaService.getStoredConfig();
35+
if (!config?.secret) {
36+
cliux.error('No MFA configuration found');
37+
process.exit(1);
4538
}
4639

4740
// Verify the configuration is valid
@@ -76,16 +69,12 @@ export default class RemoveMFACommand extends BaseCommand<typeof RemoveMFAComman
7669
this.mfaService.removeConfig();
7770
cliux.success('Secret has been removed successfully');
7871
} catch (error) {
79-
this.logger.error('Failed to remove secret configuration', { error });
80-
throw new MFAError('Failed to remove secret configuration');
72+
handleAndLogError(error, { module: 'config:mfa:remove' });
73+
process.exit(1);
8174
}
8275
} catch (error) {
83-
if (error instanceof MFAError) {
84-
cliux.error(error.message);
85-
} else {
86-
cliux.error('Failed to remove secret configuration');
87-
}
88-
throw error;
76+
handleAndLogError(error, { module: 'config:mfa:remove' });
77+
process.exit(1);
8978
}
9079
}
9180
}

packages/contentstack-config/src/services/mfa/mfa.service.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { configHandler, NodeCrypto, log } from '@contentstack/cli-utilities';
1+
import { configHandler, NodeCrypto, log, cliux } from '@contentstack/cli-utilities';
22
import { authenticator } from 'otplib';
3-
import { MFAConfig, MFAError } from './mfa.types';
3+
import { MFAConfig } from './mfa.types';
44
import { IMFAService } from './mfa-service.interface';
55

66
export class MFAService implements IMFAService {
@@ -58,7 +58,7 @@ export class MFAService implements IMFAService {
5858
return this.encrypter.encrypt(secret.trim().toUpperCase());
5959
} catch (error) {
6060
this.logger.error('Secret encryption failed', { error });
61-
throw new MFAError('Failed to encrypt secret');
61+
throw new Error('Failed to encrypt secret');
6262
}
6363
}
6464

@@ -67,7 +67,7 @@ export class MFAService implements IMFAService {
6767
return this.encrypter.decrypt(encryptedSecret);
6868
} catch (error) {
6969
this.logger.error('Secret decryption failed', { error });
70-
throw new MFAError('Failed to decrypt secret');
70+
throw new Error('Failed to decrypt secret');
7171
}
7272
}
7373

@@ -88,7 +88,7 @@ export class MFAService implements IMFAService {
8888
return config?.secret ? config as MFAConfig : null;
8989
} catch (error) {
9090
this.logger.error('Failed to read config', { error });
91-
throw new MFAError('Failed to read configuration');
91+
throw new Error('Failed to read configuration');
9292
}
9393
}
9494

@@ -101,7 +101,7 @@ export class MFAService implements IMFAService {
101101
configHandler.set('mfa', updatedConfig);
102102
} catch (error) {
103103
this.logger.error('Failed to store config', { error });
104-
throw new MFAError('Failed to store configuration');
104+
throw new Error('Failed to store configuration');
105105
}
106106
}
107107

@@ -110,7 +110,7 @@ export class MFAService implements IMFAService {
110110
configHandler.delete('mfa');
111111
} catch (error) {
112112
this.logger.error('Failed to remove config', { error });
113-
throw new MFAError('Failed to remove configuration');
113+
throw new Error('Failed to remove configuration');
114114
}
115115
}
116116

@@ -119,7 +119,7 @@ export class MFAService implements IMFAService {
119119
return authenticator.generate(secret.trim().toUpperCase());
120120
} catch (error) {
121121
this.logger.error('Failed to generate code', { error });
122-
throw new MFAError('Failed to generate code');
122+
throw new Error('Failed to generate code');
123123
}
124124
}
125125

packages/contentstack-config/src/services/mfa/mfa.types.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,4 @@ export interface MFAConfig {
66
export interface MFAValidationResult {
77
isValid: boolean;
88
error?: string;
9-
}
10-
11-
export class MFAError extends Error {
12-
constructor(message: string) {
13-
super(message);
14-
this.name = 'MFAError';
15-
Object.setPrototypeOf(this, MFAError.prototype);
16-
}
179
}

0 commit comments

Comments
 (0)