Add configurable multi-provider SSO, SSO-only mode, and JIT controls via auth.yml (#441)

* Add configuration and logic for dynamic SSO provider support and stricter JIT account creation

- Introduced `config/auth.yml` for centralized auth configuration and documentation.
- Added support for multiple SSO providers, including Google, GitHub, and OpenID Connect.
- Implemented stricter JIT SSO account creation modes (`create_and_link` vs `link_only`).
- Enabled optional restriction of JIT creation by allowed email domains.
- Enhanced OmniAuth initializer for dynamic provider setup and better configurability.
- Refined login UI to handle local login disabling and emergency super-admin override.
- Updated account creation flow to respect JIT mode and domain checks.
- Added tests for SSO account creation, login form visibility, and emergency overrides.

# Conflicts:
#	app/controllers/sessions_controller.rb

* remove non-translation

* Refactor authentication views to use translation keys and update locale files

- Extracted hardcoded strings in `oidc_accounts/link.html.erb` and `sessions/new.html.erb` into translation keys for better localization support.
- Added missing translations for English and Spanish in `sessions` and `oidc_accounts` locale files.

* Enhance OmniAuth provider configuration and refine local login override logic

- Updated OmniAuth initializer to support dynamic provider configuration with `name` and scoped parameters for Google and GitHub.
- Improved local login logic to enforce stricter handling of super-admin override when local login is disabled.
- Added test for invalid super-admin override credentials.

* Document Google sign-in configuration for local development and self-hosted environments

---------

Co-authored-by: Josh Waldrep <joshua.waldrep5+github@gmail.com>
This commit is contained in:
LPW
2025-12-23 18:15:53 -05:00
committed by GitHub
parent 8972cb59f0
commit b23711ae0d
19 changed files with 788 additions and 97 deletions

View File

@@ -5,24 +5,53 @@ class SessionsController < ApplicationController
layout "auth"
def new
demo = demo_config
@prefill_demo_credentials = demo_host_match?(demo)
if @prefill_demo_credentials
@email = params[:email].presence || demo["email"]
@password = params[:password].presence || demo["password"]
else
begin
demo = Rails.application.config_for(:demo)
@prefill_demo_credentials = demo_host_match?(demo)
if @prefill_demo_credentials
@email = params[:email].presence || demo["email"]
@password = params[:password].presence || demo["password"]
else
@email = params[:email]
@password = params[:password]
end
rescue RuntimeError, Errno::ENOENT, Psych::SyntaxError
# Demo config file missing or malformed - disable demo credential prefilling
@prefill_demo_credentials = false
@email = params[:email]
@password = params[:password]
end
end
def create
if user = User.authenticate_by(email: params[:email], password: params[:password])
user = nil
if AuthConfig.local_login_enabled?
user = User.authenticate_by(email: params[:email], password: params[:password])
else
# Local login is disabled. Only allow attempts when an emergency super-admin
# override is enabled and the email belongs to a super-admin.
if AuthConfig.local_admin_override_enabled?
candidate = User.find_by(email: params[:email])
unless candidate&.super_admin?
redirect_to new_session_path, alert: t("sessions.create.local_login_disabled")
return
end
user = User.authenticate_by(email: params[:email], password: params[:password])
else
redirect_to new_session_path, alert: t("sessions.create.local_login_disabled")
return
end
end
if user
if user.otp_required?
log_super_admin_override_login(user)
session[:mfa_user_id] = user.id
redirect_to verify_mfa_path
else
log_super_admin_override_login(user)
@session = create_session_for(user)
redirect_to root_path
end
@@ -85,4 +114,20 @@ class SessionsController < ApplicationController
def set_session
@session = Current.user.sessions.find(params[:id])
end
def log_super_admin_override_login(user)
# Only log when local login is globally disabled but an emergency
# super-admin override is enabled.
return if AuthConfig.local_login_enabled?
return unless AuthConfig.local_admin_override_enabled?
return unless user&.super_admin?
Rails.logger.info("[AUTH] Super admin override login: user_id=#{user.id} email=#{user.email}")
end
def demo_host_match?(demo)
return false unless demo.present? && demo["hosts"].present?
demo["hosts"].include?(request.host)
end
end