diff --git a/app/javascript/dashboard/composables/useWhatsappReauthorization.js b/app/javascript/dashboard/composables/useWhatsappReauthorization.js
new file mode 100644
index 000000000..47ca43b18
--- /dev/null
+++ b/app/javascript/dashboard/composables/useWhatsappReauthorization.js
@@ -0,0 +1,317 @@
+import { ref, computed } from 'vue';
+import { useStore } from 'vuex';
+import { useRouter } from 'vue-router';
+import { useI18n } from 'vue-i18n';
+import { useAlert } from 'dashboard/composables';
+import Auth from 'dashboard/api/auth';
+
+export function useWhatsappReauthorization(inboxId) {
+ const store = useStore();
+ const router = useRouter();
+ const { t } = useI18n();
+
+ // State
+ const fbSdkLoaded = ref(false);
+ const isProcessing = ref(false);
+ const processingMessage = ref('');
+ const authCodeReceived = ref(false);
+ const authCode = ref(null);
+ const businessData = ref(null);
+ const isAuthenticating = ref(false);
+
+ // Computed
+ const authHeaders = computed(() => {
+ if (Auth.hasAuthCookie()) {
+ const {
+ 'access-token': accessToken,
+ 'token-type': tokenType,
+ client,
+ expiry,
+ uid,
+ } = Auth.getAuthData();
+ return {
+ 'access-token': accessToken,
+ 'token-type': tokenType,
+ client,
+ expiry,
+ uid,
+ };
+ }
+ return {};
+ });
+
+ const showLoader = computed(
+ () => isAuthenticating.value || isProcessing.value
+ );
+
+ // Error handling
+ const handleReauthorizationError = data => {
+ isProcessing.value = false;
+ authCodeReceived.value = false;
+ isAuthenticating.value = false;
+
+ const errorMessage =
+ data.error ||
+ data.message ||
+ t('INBOX_MGMT.SETTINGS.WHATSAPP_REAUTHORIZE.ERROR');
+ useAlert(errorMessage);
+ };
+
+ const handleReauthorizationCancellation = () => {
+ isProcessing.value = false;
+ authCodeReceived.value = false;
+ isAuthenticating.value = false;
+
+ useAlert(t('INBOX_MGMT.SETTINGS.WHATSAPP_REAUTHORIZE.CANCELLED'));
+ };
+
+ const handleReauthorizationSuccess = async () => {
+ isProcessing.value = false;
+ isAuthenticating.value = false;
+
+ // Refresh the inbox to update reauthorization status
+ await store.dispatch('inboxes/get');
+ useAlert(t('INBOX_MGMT.SETTINGS.WHATSAPP_REAUTHORIZE.SUCCESS'));
+
+ // Reload the page to reflect the changes
+ router.go(0);
+ };
+
+ // Reauthorization flow
+ const completeReauthorizationFlow = async businessDataParam => {
+ if (!authCodeReceived.value || !authCode.value) {
+ handleReauthorizationError({
+ error: t('INBOX_MGMT.SETTINGS.WHATSAPP_REAUTHORIZE.AUTH_NOT_COMPLETED'),
+ });
+ return;
+ }
+
+ isProcessing.value = true;
+ processingMessage.value = t(
+ 'INBOX_MGMT.SETTINGS.WHATSAPP_REAUTHORIZE.PROCESSING'
+ );
+
+ try {
+ // Send reauthorization request
+ const accountId = store.getters.getCurrentAccountId;
+ const response = await fetch('/whatsapp/reauthorize', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'X-CSRF-Token': document
+ .querySelector('meta[name="csrf-token"]')
+ ?.getAttribute('content'),
+ ...authHeaders.value,
+ },
+ body: JSON.stringify({
+ account_id: accountId,
+ inbox_id: inboxId,
+ code: authCode.value,
+ business_id: businessDataParam.business_id,
+ waba_id: businessDataParam.waba_id,
+ phone_number_id: businessDataParam.phone_number_id,
+ }),
+ });
+
+ const responseData = await response.json();
+
+ if (response.ok) {
+ // Clear the stored auth code for security
+ authCode.value = null;
+
+ // Handle synchronous success response
+ handleReauthorizationSuccess();
+ } else {
+ throw new Error(responseData.message || responseData.error);
+ }
+ } catch (error) {
+ handleReauthorizationError({ error: error.message });
+ }
+ };
+
+ const isValidBusinessData = businessDataLocal => {
+ return (
+ businessDataLocal &&
+ (businessDataLocal.business_id || businessDataLocal.businessId) &&
+ (businessDataLocal.waba_id || businessDataLocal.wabaId)
+ );
+ };
+
+ // Message handling
+ const handleEmbeddedSignupData = async data => {
+ // Handle different embedded signup events per Facebook documentation
+ if (data.event === 'FINISH') {
+ // Facebook might send business data in different structures
+ let businessDataLocal = data.data;
+
+ // If data.data doesn't exist, try other possible structures
+ if (!businessDataLocal) {
+ businessDataLocal = data.business_data || data.details || data;
+ }
+
+ // Validate we have the required business information
+ if (isValidBusinessData(businessDataLocal)) {
+ // Normalize the data structure to match our backend expectations
+ const normalizedData = {
+ business_id:
+ businessDataLocal.business_id || businessDataLocal.businessId,
+ waba_id: businessDataLocal.waba_id || businessDataLocal.wabaId,
+ phone_number_id:
+ businessDataLocal.phone_number_id ||
+ businessDataLocal.phoneNumberId ||
+ businessDataLocal.phone_id,
+ };
+
+ // Store business data
+ businessData.value = normalizedData;
+ // Check if we already have auth code and process immediately
+ if (authCodeReceived.value && authCode.value) {
+ await completeReauthorizationFlow(normalizedData);
+ } else {
+ processingMessage.value = t(
+ 'INBOX_MGMT.SETTINGS.WHATSAPP_REAUTHORIZE.WAITING_FOR_AUTH'
+ );
+ }
+ } else {
+ handleReauthorizationError({
+ error: t(
+ 'INBOX_MGMT.SETTINGS.WHATSAPP_REAUTHORIZE.INVALID_BUSINESS_DATA'
+ ),
+ });
+ }
+ } else if (data.event === 'CANCEL') {
+ handleReauthorizationCancellation();
+ } else if (data.event === 'error') {
+ handleReauthorizationError({
+ error:
+ data.error_message ||
+ t('INBOX_MGMT.SETTINGS.WHATSAPP_REAUTHORIZE.ERROR'),
+ });
+ }
+ };
+
+ const fbLoginCallback = response => {
+ if (response.authResponse && response.authResponse.code) {
+ // Authorization code received from Facebook
+ authCode.value = response.authResponse.code;
+ authCodeReceived.value = true;
+ processingMessage.value = t(
+ 'INBOX_MGMT.SETTINGS.WHATSAPP_REAUTHORIZE.WAITING_FOR_BUSINESS_INFO'
+ );
+
+ // Check if we already have business data and process immediately
+ if (businessData.value) {
+ completeReauthorizationFlow(businessData.value);
+ }
+ } else if (response.error) {
+ handleReauthorizationError({ error: response.error });
+ } else {
+ isProcessing.value = false;
+ isAuthenticating.value = false;
+ useAlert(t('INBOX_MGMT.SETTINGS.WHATSAPP_REAUTHORIZE.CANCELLED'));
+ }
+ };
+
+ const handleSignupMessage = event => {
+ // Handle Facebook embedded signup message events
+ try {
+ const originUrl = new URL(event.origin);
+ const allowedHosts = ['facebook.com', 'www.facebook.com'];
+ if (!allowedHosts.includes(originUrl.hostname)) return;
+ } catch (error) {
+ // Invalid origin URL, reject the event
+ return;
+ }
+
+ try {
+ const data = JSON.parse(event.data);
+
+ if (data.type === 'WA_EMBEDDED_SIGNUP') {
+ handleEmbeddedSignupData(data);
+ }
+ } catch (error) {
+ // Handle non-JSON messages silently
+ }
+ };
+
+ // Facebook SDK
+ const loadFacebookSdk = () => {
+ if (window.FB) {
+ fbSdkLoaded.value = true;
+ return;
+ }
+ const script = document.createElement('script');
+ script.src = 'https://connect.facebook.net/en_US/sdk.js';
+ script.async = true;
+ script.defer = true;
+ script.onload = () => {
+ window.FB.init({
+ appId: window.chatwootConfig?.whatsappAppId,
+ status: true,
+ xfbml: true,
+ version: window.chatwootConfig?.whatsappApiVersion || 'v22.0',
+ });
+ fbSdkLoaded.value = true;
+ };
+ document.body.appendChild(script);
+ };
+
+ const launchReauthorization = () => {
+ processingMessage.value = t(
+ 'INBOX_MGMT.SETTINGS.WHATSAPP_REAUTHORIZE.AUTH_PROCESSING'
+ );
+
+ if (!window.FB) {
+ loadFacebookSdk();
+ setTimeout(() => launchReauthorization(), 1000);
+ return;
+ }
+
+ isAuthenticating.value = true;
+
+ // Following Facebook's embedded signup documentation
+ // Using auth_type: 'reauthorize' to force reauthorization
+ window.FB.login(fbLoginCallback, {
+ config_id: window.chatwootConfig?.whatsappConfigurationId,
+ response_type: 'code',
+ override_default_response_type: true,
+ auth_type: 'reauthorize',
+ extras: {
+ setup: {},
+ featureType: '', // Leave empty for default flow
+ sessionInfoVersion: '3',
+ },
+ });
+ };
+
+ // Lifecycle
+ const setupMessageListener = () => {
+ window.addEventListener('message', handleSignupMessage);
+ };
+
+ const cleanupMessageListener = () => {
+ window.removeEventListener('message', handleSignupMessage);
+ };
+
+ // Initialize
+ const initialize = () => {
+ loadFacebookSdk();
+ setupMessageListener();
+ };
+
+ return {
+ // State
+ fbSdkLoaded,
+ isProcessing,
+ processingMessage,
+ isAuthenticating,
+
+ // Computed
+ showLoader,
+
+ // Methods
+ launchReauthorization,
+ initialize,
+ cleanupMessageListener,
+ };
+}
diff --git a/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json
index eb999a0e5..5c6eb82cf 100644
--- a/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json
@@ -600,7 +600,24 @@
"WHATSAPP_SECTION_UPDATE_BUTTON": "Update",
"WHATSAPP_WEBHOOK_TITLE": "Webhook Verification Token",
"WHATSAPP_WEBHOOK_SUBHEADER": "This token is used to verify the authenticity of the webhook endpoint.",
- "UPDATE_PRE_CHAT_FORM_SETTINGS": "Update Pre Chat Form Settings"
+ "WHATSAPP_RECONFIGURE_TITLE": "Reconfigure WhatsApp",
+ "WHATSAPP_RECONFIGURE_SUBHEADER": "Update your WhatsApp Business account configuration using the embedded signup flow.",
+ "WHATSAPP_RECONFIGURE_BUTTON": "Reconfigure with WhatsApp",
+ "WHATSAPP_RECONFIGURE_ERROR": "Failed to initiate WhatsApp reconfiguration. Please try again.",
+ "UPDATE_PRE_CHAT_FORM_SETTINGS": "Update Pre Chat Form Settings",
+ "WHATSAPP_REAUTHORIZE": {
+ "TITLE": "Reauthorize WhatsApp Connection",
+ "DESCRIPTION": "Your WhatsApp Business account needs to be reauthorized to continue receiving messages. Click the button below to reconnect your account.",
+ "AUTH_PROCESSING": "Processing WhatsApp reauthorization...",
+ "WAITING_FOR_BUSINESS_INFO": "Waiting for business information...",
+ "WAITING_FOR_AUTH": "Waiting for authentication...",
+ "PROCESSING": "Updating your WhatsApp configuration...",
+ "SUCCESS": "WhatsApp successfully reauthorized!",
+ "ERROR": "Failed to reauthorize WhatsApp. Please try again.",
+ "CANCELLED": "WhatsApp reauthorization was cancelled.",
+ "AUTH_NOT_COMPLETED": "Authentication not completed. Please try again.",
+ "INVALID_BUSINESS_DATA": "Invalid business data received. Please try again."
+ }
},
"HELP_CENTER": {
"LABEL": "Help Center",
@@ -620,6 +637,12 @@
"MESSAGE_SUCCESS": "Reconnection successful",
"MESSAGE_ERROR": "There was an error, please try again"
},
+ "WHATSAPP_REAUTHORIZE": {
+ "TITLE": "Reauthorize WhatsApp",
+ "SUBTITLE": "Your WhatsApp connection has expired, please reconnect your WhatsApp Business account to continue services",
+ "MESSAGE_SUCCESS": "WhatsApp reconnection successful",
+ "MESSAGE_ERROR": "There was an error reconnecting WhatsApp, please try again"
+ },
"PRE_CHAT_FORM": {
"DESCRIPTION": "Pre chat forms enable you to capture user information before they start conversation with you.",
"SET_FIELDS": "Pre chat form fields",
diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue b/app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue
index a3f60c6d5..783e0b881 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue
+++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue
@@ -11,6 +11,7 @@ import InstagramReauthorize from './channels/instagram/Reauthorize.vue';
import DuplicateInboxBanner from './channels/instagram/DuplicateInboxBanner.vue';
import MicrosoftReauthorize from './channels/microsoft/Reauthorize.vue';
import GoogleReauthorize from './channels/google/Reauthorize.vue';
+import WhatsappReauthorize from './channels/whatsapp/Reauthorize.vue';
import PreChatFormSettings from './PreChatForm/Settings.vue';
import WeeklyAvailability from './components/WeeklyAvailability.vue';
import GreetingsEditor from 'shared/components/GreetingsEditor.vue';
@@ -45,6 +46,7 @@ export default {
NextButton,
InstagramReauthorize,
DuplicateInboxBanner,
+ WhatsappReauthorize,
Editor,
},
mixins: [inboxMixin],
@@ -231,6 +233,11 @@ export default {
facebookUnauthorized() {
return this.isAFacebookInbox && this.inbox.reauthorization_required;
},
+ whatsappUnauthorized() {
+ return (
+ this.isAWhatsAppCloudChannel && this.inbox.reauthorization_required
+ );
+ },
googleUnauthorized() {
const isLegacyInbox = ['imap.gmail.com', 'imap.google.com'].includes(
this.inbox.imap_address
@@ -410,6 +417,7 @@ export default {
+
+import { onMounted, onBeforeUnmount } from 'vue';
+import InboxReconnectionRequired from '../../components/InboxReconnectionRequired.vue';
+import LoadingState from 'dashboard/components/widgets/LoadingState.vue';
+import { useWhatsappReauthorization } from 'dashboard/composables/useWhatsappReauthorization';
+
+const props = defineProps({
+ inbox: {
+ type: Object,
+ required: true,
+ },
+});
+const {
+ processingMessage,
+ showLoader,
+ launchReauthorization,
+ initialize,
+ cleanupMessageListener,
+} = useWhatsappReauthorization(props.inbox.id);
+
+onMounted(() => {
+ initialize();
+});
+
+onBeforeUnmount(() => {
+ cleanupMessageListener();
+});
+
+
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/settingsPage/ConfigurationPage.vue b/app/javascript/dashboard/routes/dashboard/settings/inbox/settingsPage/ConfigurationPage.vue
index bc5355c3c..c91da7b26 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/inbox/settingsPage/ConfigurationPage.vue
+++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/settingsPage/ConfigurationPage.vue
@@ -7,6 +7,7 @@ import SmtpSettings from '../SmtpSettings.vue';
import { useVuelidate } from '@vuelidate/core';
import { required } from '@vuelidate/validators';
import NextButton from 'dashboard/components-next/button/Button.vue';
+import { useWhatsappReauthorization } from 'dashboard/composables/useWhatsappReauthorization';
export default {
components: {
@@ -34,6 +35,14 @@ export default {
validations: {
whatsAppInboxAPIKey: { required },
},
+ computed: {
+ isWhatsAppCloudWithEmbeddedSignup() {
+ return (
+ this.inbox.provider === 'whatsapp_cloud' &&
+ this.inbox.provider_config?.source === 'embedded_signup'
+ );
+ },
+ },
watch: {
inbox() {
this.setDefaults();
@@ -83,6 +92,18 @@ export default {
useAlert(this.$t('INBOX_MGMT.EDIT.API.ERROR_MESSAGE'));
}
},
+ reconfigureWhatsApp() {
+ // Initialize the WhatsApp reauthorization flow
+ const { launchReauthorization, initialize } = useWhatsappReauthorization(
+ this.inbox.id
+ );
+
+ // Initialize and launch
+ initialize();
+ launchReauthorization();
+
+ // Note: cleanup will happen when component unmounts or user navigates away
+ },
},
};
@@ -208,7 +229,21 @@ export default {
>
+
+
+ {{ $t('INBOX_MGMT.SETTINGS_POPUP.WHATSAPP_RECONFIGURE_BUTTON') }}
+
+
+
+