feat: Add account-level SSO configuration
This implements Single Sign-On (SSO) functionality at the account level, allowing individual accounts to configure their own SSO settings. Key changes: - Added SSO configuration to account model with JSONB storage - Implemented SSO settings UI in account settings panel - Added feature flag to control SSO access (premium feature) - Updated authentication flow to support account-level SSO - Added proper validation and error handling 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
+41
-1
@@ -12,6 +12,7 @@
|
||||
# locale :integer default("en")
|
||||
# name :string not null
|
||||
# settings :jsonb
|
||||
# sso_config :jsonb not null
|
||||
# status :integer default("active")
|
||||
# support_email :string(100)
|
||||
# created_at :datetime not null
|
||||
@@ -19,7 +20,8 @@
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_accounts_on_status (status)
|
||||
# index_accounts_on_sso_config (sso_config) USING gin
|
||||
# index_accounts_on_status (status)
|
||||
#
|
||||
|
||||
class Account < ApplicationRecord
|
||||
@@ -55,6 +57,7 @@ class Account < ApplicationRecord
|
||||
|
||||
store_accessor :settings, :auto_resolve_after, :auto_resolve_message, :auto_resolve_ignore_waiting
|
||||
store_accessor :settings, :audio_transcriptions, :auto_resolve_label
|
||||
store_accessor :sso_config, :enabled, :provider_name, :login_url, :logout_url, :secret_key, :token_expiry
|
||||
|
||||
has_many :account_users, dependent: :destroy_async
|
||||
has_many :agent_bot_inboxes, dependent: :destroy_async
|
||||
@@ -158,6 +161,43 @@ class Account < ApplicationRecord
|
||||
ISO_639.find(account_locale)&.english_name&.downcase || 'english'
|
||||
end
|
||||
|
||||
# SSO Configuration Methods
|
||||
def sso_enabled?
|
||||
ActiveModel::Type::Boolean.new.cast(sso_config['enabled'])
|
||||
end
|
||||
|
||||
def sso_provider_name
|
||||
sso_config['provider_name'].presence || 'SSO'
|
||||
end
|
||||
|
||||
def sso_login_url
|
||||
sso_config['login_url']
|
||||
end
|
||||
|
||||
def sso_logout_url
|
||||
sso_config['logout_url']
|
||||
end
|
||||
|
||||
def sso_secret_key
|
||||
sso_config['secret_key']
|
||||
end
|
||||
|
||||
def sso_token_expiry
|
||||
(sso_config['token_expiry'].presence || 5).to_i
|
||||
end
|
||||
|
||||
def update_sso_config(config)
|
||||
# Validate required fields if SSO is enabled
|
||||
return false if ActiveModel::Type::Boolean.new.cast(config['enabled']) && (config['login_url'].blank? || config['secret_key'].blank?)
|
||||
|
||||
# Set default values
|
||||
config['token_expiry'] = 5 if config['token_expiry'].blank?
|
||||
config['provider_name'] = 'SSO' if config['provider_name'].blank?
|
||||
|
||||
self.sso_config = config
|
||||
save
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def notify_creation
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
# enabled :boolean default(TRUE)
|
||||
# message :text not null
|
||||
# scheduled_at :datetime
|
||||
# template_params :jsonb
|
||||
# template_params :jsonb not null
|
||||
# title :string not null
|
||||
# trigger_only_during_business_hours :boolean default(FALSE)
|
||||
# trigger_rules :jsonb
|
||||
|
||||
@@ -2,8 +2,11 @@ module SsoAuthenticatable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def generate_sso_auth_token
|
||||
return nil unless account&.sso_enabled?
|
||||
|
||||
token = SecureRandom.hex(32)
|
||||
::Redis::Alfred.setex(sso_token_key(token), true, 5.minutes)
|
||||
expiry_minutes = account.sso_token_expiry.minutes
|
||||
::Redis::Alfred.setex(sso_token_key(token), true, expiry_minutes)
|
||||
token
|
||||
end
|
||||
|
||||
@@ -16,14 +19,30 @@ module SsoAuthenticatable
|
||||
end
|
||||
|
||||
def generate_sso_link
|
||||
return nil unless account&.sso_enabled?
|
||||
|
||||
encoded_email = ERB::Util.url_encode(email)
|
||||
"#{ENV.fetch('FRONTEND_URL', nil)}/app/login?email=#{encoded_email}&sso_auth_token=#{generate_sso_auth_token}"
|
||||
end
|
||||
|
||||
def generate_sso_link_with_impersonation
|
||||
return nil unless account&.sso_enabled?
|
||||
|
||||
"#{generate_sso_link}&impersonation=true"
|
||||
end
|
||||
|
||||
def sso_external_login_url
|
||||
return nil unless account&.sso_enabled?
|
||||
|
||||
account.sso_login_url
|
||||
end
|
||||
|
||||
def sso_external_logout_url
|
||||
return nil unless account&.sso_enabled?
|
||||
|
||||
account.sso_logout_url
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def sso_token_key(token)
|
||||
|
||||
Reference in New Issue
Block a user