Module: AuthenticatesWithTwoFactor

Extended by:
ActiveSupport::Concern
Includes:
Authn::WebauthnInstrumentation
Included in:
Profiles::AccountsController, Profiles::PasskeysController, Profiles::TwoFactorAuthsController, SessionsController
Defined in:
app/controllers/concerns/authenticates_with_two_factor.rb

Overview

AuthenticatesWithTwoFactor

Controller concern to handle two-factor authentication

Constant Summary

Constants included from Authn::WebauthnInstrumentation

Authn::WebauthnInstrumentation::PASSKEY_EVENT_TRACKING_ENTRY_POINT, Authn::WebauthnInstrumentation::PASSKEY_EVENT_TRACKING_STATUS

Instance Method Summary collapse

Methods included from Authn::WebauthnInstrumentation

#track_passkey_internal_event

Methods included from Gitlab::InternalEventsTracking

#track_internal_event

Instance Method Details

#authenticate_with_two_factorObject



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'app/controllers/concerns/authenticates_with_two_factor.rb', line 58

def authenticate_with_two_factor
  user = self.resource = find_user

  # If user does not complete two-factor authentication and authenticates as
  # another user within the same session, we should reset-two factor data
  # in the session, see
  #   - https://gitlab.com/gitlab-org/gitlab-foss/-/issues/14900
  #   - https://gitlab.com/gitlab-org/gitlab/-/issues/20302
  clear_two_factor_attempt! if session[:otp_user_id] != user.id

  return handle_locked_user(user) unless user.can?(:log_in)
  return handle_changed_user_password(user) if user_password_changed?(user)

  if user_params[:otp_attempt].present? && session[:otp_user_id]
    authenticate_with_two_factor_via_otp(user)
  elsif user_params[:device_response].present? && session[:otp_user_id]
    authenticate_with_two_factor_via_webauthn(user)
  elsif user && user.valid_password?(user_params[:password])
    prompt_for_two_factor(user)
  end
rescue ActiveRecord::RecordInvalid => e
  # We expect User to always be valid.
  # Otherwise, raise internal server error instead of unprocessable entity to improve observability/alerting
  if e.record.is_a?(User)
    raise e.message
  else
    raise e
  end
end

#handle_locked_user(user) ⇒ Object



40
41
42
43
44
# File 'app/controllers/concerns/authenticates_with_two_factor.rb', line 40

def handle_locked_user(user)
  clear_two_factor_attempt!

  locked_user_redirect(user)
end

#handle_passwordless_flowObject



50
51
52
53
54
55
56
# File 'app/controllers/concerns/authenticates_with_two_factor.rb', line 50

def handle_passwordless_flow
  if passwordless_passkey_params[:device_response].present?
    authenticate_with_passwordless_authentication_via_passkey
  else
    prompt_for_passwordless_authentication_via_passkey
  end
end

#locked_user_redirect(user) ⇒ Object



46
47
48
# File 'app/controllers/concerns/authenticates_with_two_factor.rb', line 46

def locked_user_redirect(user)
  redirect_to new_user_session_path, alert: locked_user_redirect_alert(user)
end

#prompt_for_passwordless_authentication_via_passkeyObject



33
34
35
36
37
38
# File 'app/controllers/concerns/authenticates_with_two_factor.rb', line 33

def prompt_for_passwordless_authentication_via_passkey
  add_gon_variables
  setup_passkey_authentication

  render 'devise/sessions/passkeys'
end

#prompt_for_two_factor(user) ⇒ Object

Store the user’s ID in the session for later retrieval and render the two factor code prompt

The user must have been authenticated with a valid login and password before calling this method!

user - User record

Returns nil



19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'app/controllers/concerns/authenticates_with_two_factor.rb', line 19

def prompt_for_two_factor(user)
  @user = user # rubocop:disable Gitlab/ModuleWithInstanceVariables -- Set @user for Devise views

  return handle_locked_user(user) unless user.can?(:log_in)

  session[:otp_user_id] = user.id
  session[:user_password_hash] = Digest::SHA256.hexdigest(user.encrypted_password)

  add_gon_variables
  setup_webauthn_authentication(user)

  render 'devise/sessions/two_factor'
end