mirror of https://github.com/docusealco/docuseal
- Implement SsoLoginController for token-based SSO login - Add /sso/login route endpoint - Auto-create users from JWT payload if email doesn't exist - Bypass standard authentication flow for SSO userspull/572/head
parent
b3e72f0726
commit
4bfa656864
@ -0,0 +1,141 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SsoLoginController < ApplicationController
|
||||
skip_before_action :maybe_redirect_to_setup
|
||||
skip_before_action :authenticate_user!
|
||||
skip_authorization_check
|
||||
|
||||
# SSO JWT secret key for decoding tokens
|
||||
SSO_JWT_SECRET = '6a5a4fa4733123c991256f5b0f2221fbe8f7b4c210f74fba621b44c9d5e9f8b6'.freeze
|
||||
|
||||
def login
|
||||
token = params[:token]
|
||||
|
||||
unless token.present?
|
||||
return redirect_to root_path, alert: 'Missing authentication token'
|
||||
end
|
||||
|
||||
begin
|
||||
# Decode JWT token using the SSO secret key
|
||||
decoded_token = decode_sso_jwt(token)
|
||||
|
||||
email = decoded_token['email']&.downcase
|
||||
first_name = decoded_token['first_name']
|
||||
last_name = decoded_token['last_name']
|
||||
|
||||
unless email.present?
|
||||
return redirect_to root_path, alert: 'Invalid token: email missing'
|
||||
end
|
||||
|
||||
# Find or create user
|
||||
user = find_or_create_user(email, first_name, last_name)
|
||||
|
||||
if user
|
||||
# Sign in the user
|
||||
sign_in(user)
|
||||
|
||||
# Redirect to dashboard
|
||||
redirect_to root_path, notice: 'Signed in successfully'
|
||||
else
|
||||
redirect_to root_path, alert: 'Unable to sign in'
|
||||
end
|
||||
rescue JWT::DecodeError, JWT::ExpiredSignature => e
|
||||
Rails.logger.error("SSO JWT decode error: #{e.message}")
|
||||
redirect_to root_path, alert: 'Invalid or expired authentication token'
|
||||
rescue StandardError => e
|
||||
Rails.logger.error("SSO login error: #{e.message}")
|
||||
Rails.logger.error(e.backtrace.join("\n"))
|
||||
redirect_to root_path, alert: 'An error occurred during sign in'
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def decode_sso_jwt(token)
|
||||
# Decode JWT with the SSO secret key
|
||||
decoded = JWT.decode(token, SSO_JWT_SECRET, true, { algorithm: 'HS256' })
|
||||
decoded[0] # Return the payload
|
||||
end
|
||||
|
||||
def find_or_create_user(email, first_name, last_name)
|
||||
# Try to find existing user by email
|
||||
user = User.find_by(email: email)
|
||||
|
||||
if user
|
||||
# Update user info if provided and different
|
||||
update_attrs = {}
|
||||
update_attrs[:first_name] = first_name if first_name.present? && user.first_name != first_name
|
||||
update_attrs[:last_name] = last_name if last_name.present? && user.last_name != last_name
|
||||
|
||||
user.update(update_attrs) if update_attrs.any?
|
||||
|
||||
return user
|
||||
end
|
||||
|
||||
# User doesn't exist, create a new one
|
||||
# Find or create an account
|
||||
account = find_or_create_account
|
||||
|
||||
# Generate a random password for the new user
|
||||
password = SecureRandom.hex(16)
|
||||
|
||||
# Create the new user
|
||||
user = account.users.build(
|
||||
email: email,
|
||||
first_name: first_name || '',
|
||||
last_name: last_name || '',
|
||||
password: password,
|
||||
role: User::ADMIN_ROLE
|
||||
)
|
||||
|
||||
if user.save
|
||||
user
|
||||
else
|
||||
Rails.logger.error("Failed to create user: #{user.errors.full_messages.join(', ')}")
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def find_or_create_account
|
||||
# Try to find the first active account
|
||||
account = Account.active.first
|
||||
|
||||
# If no account exists, create a default one
|
||||
unless account
|
||||
account = Account.create!(
|
||||
name: 'Default Account',
|
||||
timezone: 'UTC',
|
||||
locale: 'en-US'
|
||||
)
|
||||
|
||||
# Create encrypted configs if needed
|
||||
if EncryptedConfig.table_exists?
|
||||
app_url = Docuseal.default_url_options[:host] || request.host
|
||||
app_url = "https://#{app_url}" unless app_url.start_with?('http')
|
||||
|
||||
encrypted_configs = [
|
||||
{ key: EncryptedConfig::APP_URL_KEY, value: app_url }
|
||||
]
|
||||
|
||||
# Only add ESIGN certs if GenerateCertificate is available
|
||||
begin
|
||||
encrypted_configs << {
|
||||
key: EncryptedConfig::ESIGN_CERTS_KEY,
|
||||
value: GenerateCertificate.call.transform_values(&:to_pem)
|
||||
}
|
||||
rescue NameError, StandardError => e
|
||||
Rails.logger.warn("Could not generate ESIGN certificates: #{e.message}")
|
||||
end
|
||||
|
||||
account.encrypted_configs.create!(encrypted_configs) if encrypted_configs.any?
|
||||
end
|
||||
|
||||
# Create account configs if needed
|
||||
if AccountConfig.table_exists? && SearchEntry.table_exists?
|
||||
account.account_configs.create!(key: :fulltext_search, value: true)
|
||||
end
|
||||
end
|
||||
|
||||
account
|
||||
end
|
||||
end
|
||||
Loading…
Reference in new issue