-
Notifications
You must be signed in to change notification settings - Fork 167
Expand file tree
/
Copy pathwebauthn_setup_controller.rb
More file actions
189 lines (169 loc) · 6.17 KB
/
Copy pathwebauthn_setup_controller.rb
File metadata and controls
189 lines (169 loc) · 6.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# frozen_string_literal: true
module Users
class WebauthnSetupController < ApplicationController
include TwoFactorAuthenticatableMethods
include MfaSetupConcern
include SecureHeadersConcern
include ReauthenticationRequiredConcern
before_action :authenticate_user!
before_action :confirm_user_authenticated_for_2fa_setup
before_action :apply_secure_headers_override
before_action :set_webauthn_setup_presenter
before_action :confirm_recently_authenticated_2fa
before_action :validate_existing_platform_authenticator
helper_method :in_multi_mfa_selection_flow?
helper_method :mobile?
def new
form = WebauthnVisitForm.new(
user: current_user,
url_options: url_options,
in_mfa_selection_flow: in_multi_mfa_selection_flow?,
)
result = form.submit(new_params)
@platform_authenticator = form.platform_authenticator?
@presenter = WebauthnSetupPresenter.new(
current_user: current_user,
user_fully_authenticated: user_fully_authenticated?,
user_opted_remember_device_cookie: user_opted_remember_device_cookie,
remember_device_default: remember_device_default,
platform_authenticator: @platform_authenticator,
url_options:,
)
analytics.webauthn_setup_visit(
platform_authenticator: result.extra[:platform_authenticator],
in_account_creation_flow: user_session[:in_account_creation_flow] || false,
enabled_mfa_methods_count: result.extra[:enabled_mfa_methods_count],
)
save_challenge_in_session
@exclude_credentials = exclude_credentials
@need_to_set_up_additional_mfa = need_to_set_up_additional_mfa?
if result.errors.present?
increment_mfa_selection_attempt_count(webauthn_auth_method)
analytics.webauthn_setup_submitted(
platform_authenticator: form.platform_authenticator?,
errors: result.errors,
success: false,
)
end
flash_error(result.errors) unless result.success?
end
def confirm
increment_mfa_selection_attempt_count(webauthn_auth_method)
form = WebauthnSetupForm.new(
user: current_user,
user_session: user_session,
device_name: DeviceName.from_user_agent(request.user_agent),
)
result = form.submit(confirm_params)
@platform_authenticator = form.platform_authenticator?
@presenter = WebauthnSetupPresenter.new(
current_user: current_user,
user_fully_authenticated: user_fully_authenticated?,
user_opted_remember_device_cookie: user_opted_remember_device_cookie,
remember_device_default: remember_device_default,
platform_authenticator: @platform_authenticator,
url_options:,
)
properties = result.to_h.merge(analytics_properties)
analytics.multi_factor_auth_setup(**properties)
if result.success?
process_valid_webauthn(form)
user_session.delete(:mfa_attempts)
else
flash.now[:error] = result.first_error_message
render :new
end
end
private
def validate_existing_platform_authenticator
if platform_authenticator? && in_account_creation_flow? &&
current_user.webauthn_configurations.platform_authenticators.present?
redirect_to authentication_methods_setup_path
end
end
def webauthn_auth_method
if @platform_authenticator
TwoFactorAuthenticatable::AuthMethod::WEBAUTHN_PLATFORM
else
TwoFactorAuthenticatable::AuthMethod::WEBAUTHN
end
end
def platform_authenticator?
params[:platform] == 'true'
end
def set_webauthn_setup_presenter
@presenter = SetupPresenter.new(
current_user: current_user,
user_fully_authenticated: user_fully_authenticated?,
user_opted_remember_device_cookie: user_opted_remember_device_cookie,
remember_device_default: remember_device_default,
)
end
def flash_error(errors)
flash.now[:error] = errors.values.first.first
end
def exclude_credentials
current_user.webauthn_configurations.map(&:credential_id)
end
def save_challenge_in_session
credential_creation_options = WebAuthn::Credential.options_for_create(user: current_user)
user_session[:webauthn_challenge] = credential_creation_options.challenge.bytes.to_a
end
def process_valid_webauthn(form)
create_user_event(:webauthn_key_added)
analytics.webauthn_setup_submitted(
platform_authenticator: form.platform_authenticator?,
success: true,
)
handle_remember_device_preference(params[:remember_device])
if form.platform_authenticator?
handle_valid_verification_for_confirmation_context(
auth_method: TwoFactorAuthenticatable::AuthMethod::WEBAUTHN_PLATFORM,
)
Funnel::Registration::AddMfa.call(
current_user.id,
'webauthn_platform',
analytics,
threatmetrix_attrs,
)
flash[:success] = t('notices.webauthn_platform_configured')
else
handle_valid_verification_for_confirmation_context(
auth_method: TwoFactorAuthenticatable::AuthMethod::WEBAUTHN,
)
Funnel::Registration::AddMfa.call(
current_user.id,
'webauthn',
analytics,
threatmetrix_attrs,
)
flash[:success] = t('notices.webauthn_configured')
end
redirect_to next_setup_path || after_mfa_setup_path
end
def analytics_properties
{
in_account_creation_flow: user_session[:in_account_creation_flow] || false,
webauthn_platform_recommended: user_session[:webauthn_platform_recommended],
attempts: mfa_attempts_count,
}
end
def need_to_set_up_additional_mfa?
return false unless @platform_authenticator
in_multi_mfa_selection_flow? && mfa_selection_count < 2
end
def new_params
params.permit(:platform, :error)
end
def confirm_params
params.permit(
:attestation_object,
:authenticator_data_value,
:client_data_json,
:name,
:platform_authenticator,
:transports,
).merge(protocol: request.protocol)
end
end
end