Files
chatwoot/app/javascript/v3/api/auth.js
Tanmay Deep Sharma 4014a846f0 feat: Add the frontend support for MFA (#12372)
FE support for https://github.com/chatwoot/chatwoot/pull/12290
## Linear:
- https://github.com/chatwoot/chatwoot/issues/486

## Description
This PR implements Multi-Factor Authentication (MFA) support for user
accounts, enhancing security by requiring a second form of verification
during login. The feature adds TOTP (Time-based One-Time Password)
authentication with QR code generation and backup codes for account
recovery.

## Type of change

- [ ] New feature (non-breaking change which adds functionality)

## How Has This Been Tested?

- Added comprehensive RSpec tests for MFA controller functionality
- Tested MFA setup flow with QR code generation
- Verified OTP validation and backup code generation
- Tested login flow with MFA enabled/disabled

## Checklist:

- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my code
- [ ] I have commented on my code, particularly in hard-to-understand
areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules

---------

Co-authored-by: Pranav <pranav@chatwoot.com>
Co-authored-by: iamsivin <iamsivin@gmail.com>
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Sojan Jose <sojan@pepalo.com>
2025-09-18 21:16:06 +05:30

94 lines
2.3 KiB
JavaScript

import {
setAuthCredentials,
throwErrorMessage,
clearLocalStorageOnLogout,
} from 'dashboard/store/utils/api';
import wootAPI from './apiClient';
import { getLoginRedirectURL } from '../helpers/AuthHelper';
export const login = async ({
ssoAccountId,
ssoConversationId,
...credentials
}) => {
try {
const response = await wootAPI.post('auth/sign_in', credentials);
// Check if MFA is required
if (response.status === 206 && response.data.mfa_required) {
// Return MFA data instead of throwing error
return {
mfaRequired: true,
mfaToken: response.data.mfa_token,
};
}
setAuthCredentials(response);
clearLocalStorageOnLogout();
window.location = getLoginRedirectURL({
ssoAccountId,
ssoConversationId,
user: response.data.data,
});
return null;
} catch (error) {
// Check if it's an MFA required response
if (error.response?.status === 206 && error.response?.data?.mfa_required) {
return {
mfaRequired: true,
mfaToken: error.response.data.mfa_token,
};
}
throwErrorMessage(error);
return null;
}
};
export const register = async creds => {
try {
const response = await wootAPI.post('api/v1/accounts.json', {
account_name: creds.accountName.trim(),
user_full_name: creds.fullName.trim(),
email: creds.email,
password: creds.password,
h_captcha_client_response: creds.hCaptchaClientResponse,
});
setAuthCredentials(response);
return response.data;
} catch (error) {
throwErrorMessage(error);
}
return null;
};
export const verifyPasswordToken = async ({ confirmationToken }) => {
try {
const response = await wootAPI.post('auth/confirmation', {
confirmation_token: confirmationToken,
});
setAuthCredentials(response);
} catch (error) {
throwErrorMessage(error);
}
};
export const setNewPassword = async ({
resetPasswordToken,
password,
confirmPassword,
}) => {
try {
const response = await wootAPI.put('auth/password', {
reset_password_token: resetPasswordToken,
password_confirmation: confirmPassword,
password,
});
setAuthCredentials(response);
} catch (error) {
throwErrorMessage(error);
}
};
export const resetPassword = async ({ email }) =>
wootAPI.post('auth/password', { email });