# frozen_string_literal: true class OmniauthCallbacksController < Devise::OmniauthCallbacksController include AuthenticatesWithTwoFactor include Devise::Controllers::Rememberable include AuthHelper protect_from_forgery except: [:kerberos, :saml, :cas3, :failure], with: :exception, prepend: true def handle_omniauth omniauth_flow(Gitlab::Auth::OAuth) end AuthHelper.providers_for_base_controller.each do |provider| alias_method provider, :handle_omniauth end # Extend the standard implementation to also increment # the number of failed sign in attempts def failure if params[:username].present? && AuthHelper.form_based_provider?(failed_strategy.name) user = User.by_login(params[:username]) user&.increment_failed_attempts! end super end # Extend the standard message generation to accept our custom exception def failure_message exception = request.env["omniauth.error"] error = exception.error_reason if exception.respond_to?(:error_reason) error ||= exception.error if exception.respond_to?(:error) error ||= exception.message if exception.respond_to?(:message) error ||= request.env["omniauth.error.type"].to_s error.to_s.humanize if error end def saml omniauth_flow(Gitlab::Auth::Saml) end def omniauth_error @provider = params[:provider] @error = params[:error] render 'errors/omniauth_error', layout: "oauth_error", status: 422 end def cas3 ticket = params['ticket'] if ticket handle_service_ticket oauth['provider'], ticket end handle_omniauth end def authentiq if params['sid'] handle_service_ticket oauth['provider'], params['sid'] end handle_omniauth end def auth0 if oauth['uid'].blank? fail_auth0_login else handle_omniauth end end private def omniauth_flow(auth_module, identity_linker: nil) if fragment = request.env.dig('omniauth.params', 'redirect_fragment').presence store_redirect_fragment(fragment) end if current_user return render_403 unless link_provider_allowed?(oauth['provider']) log_audit_event(current_user, with: oauth['provider']) identity_linker ||= auth_module::IdentityLinker.new(current_user, oauth) link_identity(identity_linker) if identity_linker.changed? redirect_identity_linked elsif identity_linker.failed? redirect_identity_link_failed(identity_linker.error_message) else redirect_identity_exists end else sign_in_user_flow(auth_module::User) end end def link_identity(identity_linker) identity_linker.link end def redirect_identity_exists redirect_to after_sign_in_path_for(current_user) end def redirect_identity_link_failed(error_message) redirect_to profile_account_path, notice: _("Authentication failed: %{error_message}") % { error_message: error_message } end def redirect_identity_linked redirect_to profile_account_path, notice: _('Authentication method updated') end def handle_service_ticket(provider, ticket) Gitlab::Auth::OAuth::Session.create provider, ticket session[:service_tickets] ||= {} session[:service_tickets][provider] = ticket end def build_auth_user(auth_user_class) auth_user_class.new(oauth) end def sign_in_user_flow(auth_user_class) auth_user = build_auth_user(auth_user_class) user = auth_user.find_and_update! if auth_user.valid_sign_in? log_audit_event(user, with: oauth['provider']) set_remember_me(user) if user.two_factor_enabled? && !auth_user.bypass_two_factor? prompt_for_two_factor(user) else sign_in_and_redirect(user) end else fail_login(user) end rescue Gitlab::Auth::OAuth::User::SigninDisabledForProviderError handle_disabled_provider rescue Gitlab::Auth::OAuth::User::SignupDisabledError handle_signup_error end def handle_signup_error label = Gitlab::Auth::OAuth::Provider.label_for(oauth['provider']) message = [_("Signing in using your %{label} account without a pre-existing GitLab account is not allowed.") % { label: label }] if Gitlab::CurrentSettings.allow_signup? message << _("Create a GitLab account first, and then connect it to your %{label} account.") % { label: label } end flash[:notice] = message.join(' ') redirect_to new_user_session_path end def oauth @oauth ||= request.env['omniauth.auth'] end def fail_login(user) error_message = user.errors.full_messages.to_sentence return redirect_to omniauth_error_path(oauth['provider'], error: error_message) end def fail_auth0_login flash[:alert] = _('Wrong extern UID provided. Make sure Auth0 is configured correctly.') redirect_to new_user_session_path end def handle_disabled_provider label = Gitlab::Auth::OAuth::Provider.label_for(oauth['provider']) flash[:alert] = _("Signing in using %{label} has been disabled") % { label: label } redirect_to new_user_session_path end def log_audit_event(user, options = {}) AuditEventService.new(user, user, options) .for_authentication.security_event end def set_remember_me(user) return unless remember_me? if user.two_factor_enabled? params[:remember_me] = '1' else remember_me(user) end end def remember_me? request_params = request.env['omniauth.params'] (request_params['remember_me'] == '1') if request_params.present? end def store_redirect_fragment(redirect_fragment) key = stored_location_key_for(:user) location = session[key] if uri = parse_uri(location) uri.fragment = redirect_fragment store_location_for(:user, uri.to_s) end end end