]> cat aescling's git repositories - mastodon.git/blob - app/controllers/auth/sessions_controller.rb
Fix authentication before 2FA challenge (#11943)
[mastodon.git] / app / controllers / auth / sessions_controller.rb
1 # frozen_string_literal: true
2
3 class Auth::SessionsController < Devise::SessionsController
4 include Devise::Controllers::Rememberable
5
6 layout 'auth'
7
8 skip_before_action :require_no_authentication, only: [:create]
9 skip_before_action :require_functional!
10
11 prepend_before_action :authenticate_with_two_factor, if: :two_factor_enabled?, only: [:create]
12
13 before_action :set_instance_presenter, only: [:new]
14 before_action :set_body_classes
15
16 def new
17 Devise.omniauth_configs.each do |provider, config|
18 return redirect_to(omniauth_authorize_path(resource_name, provider)) if config.strategy.redirect_at_sign_in
19 end
20
21 super
22 end
23
24 def create
25 super do |resource|
26 remember_me(resource)
27 flash.delete(:notice)
28 end
29 end
30
31 def destroy
32 tmp_stored_location = stored_location_for(:user)
33 super
34 session.delete(:challenge_passed_at)
35 flash.delete(:notice)
36 store_location_for(:user, tmp_stored_location) if continue_after?
37 end
38
39 protected
40
41 def find_user
42 if session[:otp_user_id]
43 User.find(session[:otp_user_id])
44 else
45 user = User.authenticate_with_ldap(user_params) if Devise.ldap_authentication
46 user ||= User.authenticate_with_pam(user_params) if Devise.pam_authentication
47 user ||= User.find_for_authentication(email: user_params[:email])
48 end
49 end
50
51 def user_params
52 params.require(:user).permit(:email, :password, :otp_attempt)
53 end
54
55 def after_sign_in_path_for(resource)
56 last_url = stored_location_for(:user)
57
58 if home_paths(resource).include?(last_url)
59 root_path
60 else
61 last_url || root_path
62 end
63 end
64
65 def after_sign_out_path_for(_resource_or_scope)
66 Devise.omniauth_configs.each_value do |config|
67 return root_path if config.strategy.redirect_at_sign_in
68 end
69
70 super
71 end
72
73 def two_factor_enabled?
74 find_user&.otp_required_for_login?
75 end
76
77 def valid_otp_attempt?(user)
78 user.validate_and_consume_otp!(user_params[:otp_attempt]) ||
79 user.invalidate_otp_backup_code!(user_params[:otp_attempt])
80 rescue OpenSSL::Cipher::CipherError
81 false
82 end
83
84 def authenticate_with_two_factor
85 user = self.resource = find_user
86
87 if user_params[:otp_attempt].present? && session[:otp_user_id]
88 authenticate_with_two_factor_via_otp(user)
89 elsif user.present? && (user.encrypted_password.blank? || user.valid_password?(user_params[:password]))
90 # If encrypted_password is blank, we got the user from LDAP or PAM,
91 # so credentials are already valid
92
93 prompt_for_two_factor(user)
94 end
95 end
96
97 def authenticate_with_two_factor_via_otp(user)
98 if valid_otp_attempt?(user)
99 session.delete(:otp_user_id)
100 remember_me(user)
101 sign_in(user)
102 else
103 flash.now[:alert] = I18n.t('users.invalid_otp_token')
104 prompt_for_two_factor(user)
105 end
106 end
107
108 def prompt_for_two_factor(user)
109 session[:otp_user_id] = user.id
110 @body_classes = 'lighter'
111 render :two_factor
112 end
113
114 private
115
116 def set_instance_presenter
117 @instance_presenter = InstancePresenter.new
118 end
119
120 def set_body_classes
121 @body_classes = 'lighter'
122 end
123
124 def home_paths(resource)
125 paths = [about_path]
126 if single_user_mode? && resource.is_a?(User)
127 paths << short_account_path(username: resource.account)
128 end
129 paths
130 end
131
132 def continue_after?
133 truthy_param?(:continue)
134 end
135 end
This page took 0.127564 seconds and 6 git commands to generate.