mirror of
https://github.com/we-promise/sure.git
synced 2026-04-07 22:34:47 +00:00
* 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>
134 lines
4.1 KiB
Ruby
134 lines
4.1 KiB
Ruby
class SessionsController < ApplicationController
|
|
before_action :set_session, only: :destroy
|
|
skip_authentication only: %i[new create openid_connect failure]
|
|
|
|
layout "auth"
|
|
|
|
def new
|
|
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
|
|
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
|
|
else
|
|
flash.now[:alert] = t(".invalid_credentials")
|
|
render :new, status: :unprocessable_entity
|
|
end
|
|
end
|
|
|
|
def destroy
|
|
@session.destroy
|
|
redirect_to new_session_path, notice: t(".logout_successful")
|
|
end
|
|
|
|
def openid_connect
|
|
auth = request.env["omniauth.auth"]
|
|
|
|
# Nil safety: ensure auth and required fields are present
|
|
unless auth&.provider && auth&.uid
|
|
redirect_to new_session_path, alert: t("sessions.openid_connect.failed")
|
|
return
|
|
end
|
|
|
|
# Security fix: Look up by provider + uid, not just email
|
|
oidc_identity = OidcIdentity.find_by(provider: auth.provider, uid: auth.uid)
|
|
|
|
if oidc_identity
|
|
# Existing OIDC identity found - authenticate the user
|
|
user = oidc_identity.user
|
|
oidc_identity.record_authentication!
|
|
|
|
# MFA check: If user has MFA enabled, require verification
|
|
if user.otp_required?
|
|
session[:mfa_user_id] = user.id
|
|
redirect_to verify_mfa_path
|
|
else
|
|
@session = create_session_for(user)
|
|
redirect_to root_path
|
|
end
|
|
else
|
|
# No existing OIDC identity - need to link to account
|
|
# Store auth data in session and redirect to linking page
|
|
session[:pending_oidc_auth] = {
|
|
provider: auth.provider,
|
|
uid: auth.uid,
|
|
email: auth.info&.email,
|
|
name: auth.info&.name,
|
|
first_name: auth.info&.first_name,
|
|
last_name: auth.info&.last_name
|
|
}
|
|
redirect_to link_oidc_account_path
|
|
end
|
|
end
|
|
|
|
def failure
|
|
redirect_to new_session_path, alert: t("sessions.failure.failed")
|
|
end
|
|
|
|
private
|
|
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
|