diff --git a/app/controllers/api/v1/accounts_controller.rb b/app/controllers/api/v1/accounts_controller.rb
index 773126755..963b7dfb9 100644
--- a/app/controllers/api/v1/accounts_controller.rb
+++ b/app/controllers/api/v1/accounts_controller.rb
@@ -47,6 +47,7 @@ class Api::V1::AccountsController < Api::BaseController
@account.assign_attributes(account_params.slice(:name, :locale, :domain, :support_email))
@account.custom_attributes.merge!(custom_attributes_params)
@account.settings.merge!(settings_params)
+ @account.sso_config.merge!(sso_config_params) if sso_config_params.present? && sso_feature_enabled?
@account.custom_attributes['onboarding_step'] = 'invite_team' if @account.custom_attributes['onboarding_step'] == 'account_update'
@account.save!
end
@@ -95,6 +96,14 @@ class Api::V1::AccountsController < Api::BaseController
params.permit(:auto_resolve_after, :auto_resolve_message, :auto_resolve_ignore_waiting, :audio_transcriptions, :auto_resolve_label)
end
+ def sso_config_params
+ params.permit(sso_config: [:enabled, :provider_name, :login_url, :logout_url, :secret_key, :token_expiry])[:sso_config]
+ end
+
+ def sso_feature_enabled?
+ @account.feature_enabled?('sso')
+ end
+
def check_signup_enabled
raise ActionController::RoutingError, 'Not Found' if GlobalConfigService.load('ENABLE_ACCOUNT_SIGNUP', 'false') == 'false'
end
diff --git a/app/javascript/dashboard/featureFlags.js b/app/javascript/dashboard/featureFlags.js
index 28b6b09b7..674d5d219 100644
--- a/app/javascript/dashboard/featureFlags.js
+++ b/app/javascript/dashboard/featureFlags.js
@@ -39,6 +39,7 @@ export const FEATURE_FLAGS = {
CONTACT_CHATWOOT_SUPPORT_TEAM: 'contact_chatwoot_support_team',
WHATSAPP_EMBEDDED_SIGNUP: 'whatsapp_embedded_signup',
CAPTAIN_V2: 'captain_integration_v2',
+ SSO: 'sso',
};
export const PREMIUM_FEATURES = [
@@ -48,4 +49,5 @@ export const PREMIUM_FEATURES = [
FEATURE_FLAGS.AUDIT_LOGS,
FEATURE_FLAGS.HELP_CENTER,
FEATURE_FLAGS.CAPTAIN_V2,
+ FEATURE_FLAGS.SSO,
];
diff --git a/app/javascript/dashboard/i18n/locale/en/generalSettings.json b/app/javascript/dashboard/i18n/locale/en/generalSettings.json
index d924bffbd..024bbdda7 100644
--- a/app/javascript/dashboard/i18n/locale/en/generalSettings.json
+++ b/app/javascript/dashboard/i18n/locale/en/generalSettings.json
@@ -123,6 +123,50 @@
"CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now."
}
},
+ "SSO": {
+ "TITLE": "Single Sign-On",
+ "DESCRIPTION": "Configure SSO authentication for your organization",
+ "FORM": {
+ "ENABLE_SSO": {
+ "LABEL": "Enable SSO",
+ "HELP": "Allow users to log in using Single Sign-On"
+ },
+ "PROVIDER_NAME": {
+ "LABEL": "Provider Name",
+ "PLACEHOLDER": "e.g., 'Okta', 'Azure AD', 'Google Workspace'",
+ "ERROR": "Please enter a provider name"
+ },
+ "LOGIN_URL": {
+ "LABEL": "SSO Login URL",
+ "PLACEHOLDER": "https://your-sso-provider.com/login",
+ "ERROR": "Please enter a valid SSO login URL",
+ "HELP": "Users will be redirected to this URL for authentication"
+ },
+ "LOGOUT_URL": {
+ "LABEL": "SSO Logout URL (Optional)",
+ "PLACEHOLDER": "https://your-sso-provider.com/logout",
+ "HELP": "Users will be redirected to this URL after logout"
+ },
+ "SECRET_KEY": {
+ "LABEL": "Secret Key",
+ "PLACEHOLDER": "Enter your SSO secret key",
+ "ERROR": "Please enter a secret key",
+ "HELP": "Secret key used to verify SSO tokens"
+ },
+ "TOKEN_EXPIRY": {
+ "LABEL": "Token Expiry (minutes)",
+ "PLACEHOLDER": "5",
+ "ERROR": "Please enter a valid expiry time",
+ "HELP": "How long SSO tokens remain valid"
+ },
+ "ERROR": "Please fix the form errors"
+ },
+ "SUBMIT": "Update SSO Settings",
+ "UPDATE": {
+ "SUCCESS": "SSO settings updated successfully",
+ "ERROR": "Failed to update SSO settings"
+ }
+ },
"UPDATE_CHATWOOT": "An update {latestChatwootVersion} for Chatwoot is available. Please update your instance.",
"LEARN_MORE": "Learn more",
"PAYMENT_PENDING": "Your payment is pending. Please update your payment information to continue using Chatwoot",
diff --git a/app/javascript/dashboard/i18n/locale/en/login.json b/app/javascript/dashboard/i18n/locale/en/login.json
index ec5658db2..2d19da2fb 100644
--- a/app/javascript/dashboard/i18n/locale/en/login.json
+++ b/app/javascript/dashboard/i18n/locale/en/login.json
@@ -20,6 +20,9 @@
"BUSINESS_ACCOUNTS_ONLY": "Please use your company email address to login",
"NO_ACCOUNT_FOUND": "We couldn't find an account for your email address."
},
+ "SSO": {
+ "BUTTON_TEXT": "Login with {provider}"
+ },
"FORGOT_PASSWORD": "Forgot your password?",
"CREATE_NEW_ACCOUNT": "Create a new account",
"SUBMIT": "Login"
diff --git a/app/javascript/dashboard/routes/dashboard/settings/account/Index.vue b/app/javascript/dashboard/routes/dashboard/settings/account/Index.vue
index 69342858d..267ca0ad0 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/account/Index.vue
+++ b/app/javascript/dashboard/routes/dashboard/settings/account/Index.vue
@@ -17,6 +17,7 @@ import BuildInfo from './components/BuildInfo.vue';
import AccountDelete from './components/AccountDelete.vue';
import AutoResolve from './components/AutoResolve.vue';
import AudioTranscription from './components/AudioTranscription.vue';
+import SSOConfiguration from './components/SSOConfiguration.vue';
import SectionLayout from './components/SectionLayout.vue';
export default {
@@ -28,6 +29,7 @@ export default {
AccountDelete,
AutoResolve,
AudioTranscription,
+ SSOConfiguration,
SectionLayout,
WithLabel,
NextInput,
@@ -77,6 +79,9 @@ export default {
FEATURE_FLAGS.CAPTAIN
);
},
+ showSSOConfig() {
+ return this.isFeatureEnabledonAccount(this.accountId, FEATURE_FLAGS.SSO);
+ },
languagesSortedByCode() {
const enabledLanguages = [...this.enabledLanguages];
return enabledLanguages.sort((l1, l2) =>
@@ -244,6 +249,7 @@ export default {