Class: SessionController
Constant Summary
collapse
- ACTIVATE_USER_KEY =
"activate_user"
- FORGOT_PASSWORD_EMAIL_LIMIT_PER_DAY =
6
ApplicationController::CHALLENGE_KEY, ApplicationController::HONEYPOT_KEY, ApplicationController::LEGACY_NO_THEMES, ApplicationController::LEGACY_NO_UNOFFICIAL_PLUGINS, ApplicationController::NO_PLUGINS, ApplicationController::NO_THEMES, ApplicationController::NO_UNOFFICIAL_PLUGINS, ApplicationController::SAFE_MODE
CanonicalURL::ControllerExtensions::ALLOWED_CANONICAL_PARAMS
Instance Attribute Summary
#theme_id
Instance Method Summary
collapse
#application_layout, #can_cache_content?, #clear_notifications, #conditionally_allow_site_embedding, #current_homepage, #discourse_expires_in, #dont_cache_page, #ember_cli_required?, #fetch_user_from_params, #guardian, #handle_permalink, #handle_theme, #handle_unverified_request, #has_escaped_fragment?, #immutable_for, #login_method, #no_cookies, #perform_refresh_session, #post_ids_including_replies, #preload_json, #rate_limit_second_factor!, #redirect_with_client_support, #render_json_dump, #render_serialized, requires_plugin, #rescue_discourse_actions, #resolve_safe_mode, #secure_session, #serialize_data, #set_current_user_for_logs, #set_layout, #set_mobile_view, #set_mp_snapshot_fields, #show_browser_update?, #store_preloaded, #use_crawler_layout?, #with_resolved_locale
Methods included from VaryHeader
#ensure_vary_header
resolve_theme_id
#add_readonly_header, #allowed_in_staff_writes_only_mode?, #block_if_readonly_mode, #check_readonly_mode, #get_or_check_readonly_mode, #get_or_check_staff_writes_only_mode, included, #staff_writes_only_mode?
Methods included from Hijack
#hijack
Methods included from GlobalPath
#cdn_path, #cdn_relative_path, #full_cdn_url, #path, #upload_cdn_path
Methods included from JsonError
#create_errors_json
#canonical_url, #default_canonical, included
#clear_current_user, #current_user, has_auth_cookie?, #is_api?, #is_user_api?, #log_off_user, #log_on_user, lookup_from_env, #refresh_session
Instance Method Details
#become ⇒ Object
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
|
# File 'app/controllers/session_controller.rb', line 108
def become
raise Discourse::InvalidAccess if Rails.env.production?
raise Discourse::ReadOnly if @readonly_mode
if ENV["DISCOURSE_DEV_ALLOW_ANON_TO_IMPERSONATE"] != "1"
render(content_type: "text/plain", inline: <<~TEXT)
To enable impersonating any user without typing passwords set the following ENV var
export DISCOURSE_DEV_ALLOW_ANON_TO_IMPERSONATE=1
You can do that in your bashrc of bash profile file or the script you use to launch the web server
TEXT
return
end
user = User.find_by_username(params[:session_id])
raise "User #{params[:session_id]} not found" if user.blank?
log_on_user(user)
if params[:redirect] == "false"
render plain: "Signed in to #{params[:session_id]} successfully"
else
redirect_to path("/")
end
end
|
#create ⇒ Object
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
|
# File 'app/controllers/session_controller.rb', line 324
def create
params.require(:login)
params.require(:password)
return invalid_credentials if params[:password].length > User.max_password_length
user = User.find_by_username_or_email(normalized_login_param)
raise Discourse::ReadOnly if staff_writes_only_mode? && !user&.staff?
rate_limit_second_factor!(user)
if user.present?
password = params[:password]
if !user.confirm_password?(password)
invalid_credentials
return
end
if login_not_approved_for?(user)
render json: login_not_approved
return
end
Invite.invalidate_for_email(user.email)
if user.password_expired?(password)
render json: { error: "expired", reason: "expired" }
return
end
else
invalid_credentials
return
end
if payload = login_error_check(user)
return render json: payload
end
second_factor_auth_result = authenticate_second_factor(user)
return render(json: @second_factor_failure_payload) if !second_factor_auth_result.ok
if user.active && user.email_confirmed?
login(user, second_factor_auth_result: second_factor_auth_result)
else
not_activated(user)
end
end
|
#csrf ⇒ Object
20
21
22
|
# File 'app/controllers/session_controller.rb', line 20
def csrf
render json: { csrf: form_authenticity_token }
end
|
#current ⇒ Object
654
655
656
657
658
659
660
|
# File 'app/controllers/session_controller.rb', line 654
def current
if current_user.present?
render_serialized(current_user, CurrentUserSerializer, { login_method: login_method })
else
render body: nil, status: 404
end
end
|
#destroy ⇒ Object
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
|
# File 'app/controllers/session_controller.rb', line 662
def destroy
redirect_url = params[:return_url].presence || SiteSetting.logout_redirect.presence
sso = SiteSetting.enable_discourse_connect
only_one_authenticator =
!SiteSetting.enable_local_logins && Discourse.enabled_authenticators.length == 1
if SiteSetting.login_required && (sso || only_one_authenticator)
redirect_url ||= path("/login")
end
redirect_url ||= path("/")
event_data = {
redirect_url: redirect_url,
user: current_user,
client_ip: request&.ip,
user_agent: request&.user_agent,
}
DiscourseEvent.trigger(:before_session_destroy, event_data, **Discourse::Utils::EMPTY_KEYWORDS)
redirect_url = event_data[:redirect_url]
reset_session
log_off_user
if request.xhr?
render json: { redirect_url: redirect_url }
else
redirect_to redirect_url, allow_other_host: true
end
end
|
#email_login ⇒ Object
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
|
# File 'app/controllers/session_controller.rb', line 444
def email_login
token = params[:token]
matched_token = EmailToken.confirmable(token, scope: EmailToken.scopes[:email_login])
user = matched_token&.user
check_local_login_allowed(user: user, check_login_via_email: true)
rate_limit_second_factor!(user)
if user.present? && !authenticate_second_factor(user).ok
return render(json: @second_factor_failure_payload)
end
if user = EmailToken.confirm(token, scope: EmailToken.scopes[:email_login])
if login_not_approved_for?(user)
return render json: login_not_approved
elsif payload = login_error_check(user)
return render json: payload
else
raise Discourse::ReadOnly if staff_writes_only_mode? && !user&.staff?
user.update_timezone_if_missing(params[:timezone])
log_on_user(user)
return render json: success_json
end
end
render json: { error: I18n.t("email_login.invalid_token", base_url: Discourse.base_url) }
end
|
#email_login_info ⇒ Object
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
|
# File 'app/controllers/session_controller.rb', line 407
def email_login_info
token = params[:token]
matched_token = EmailToken.confirmable(token, scope: EmailToken.scopes[:email_login])
user = matched_token&.user
check_local_login_allowed(user: user, check_login_via_email: true)
if matched_token
response = { can_login: true, token: token, token_email: matched_token.email }
matched_user = matched_token.user
if matched_user&.totp_enabled?
response.merge!(
second_factor_required: true,
backup_codes_enabled: matched_user&.backup_codes_enabled?,
totp_enabled: matched_user&.totp_enabled?,
)
end
if matched_user&.security_keys_enabled?
DiscourseWebauthn.stage_challenge(matched_user, secure_session)
response.merge!(
DiscourseWebauthn.allowed_credentials(matched_user, secure_session).merge(
security_key_required: true,
),
)
end
render json: response
else
render json: {
can_login: false,
error: I18n.t("email_login.invalid_token", base_url: Discourse.base_url),
}
end
end
|
#forgot_password ⇒ Object
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
|
# File 'app/controllers/session_controller.rb', line 615
def forgot_password
params.require(:login)
if ScreenedIpAddress.should_block?(request.remote_ip)
return render_json_error(I18n.t("login.reset_not_allowed_from_ip_address"))
end
RateLimiter.new(nil, "forgot-password-hr-#{request.remote_ip}", 6, 1.hour).performed!
RateLimiter.new(nil, "forgot-password-min-#{request.remote_ip}", 3, 1.minute).performed!
user =
if SiteSetting.hide_email_address_taken && !current_user&.staff?
if !EmailAddressValidator.valid_value?(normalized_login_param)
raise Discourse::InvalidParameters.new(:login)
end
User.real.where(staged: false).find_by_email(Email.downcase(normalized_login_param))
else
User.real.where(staged: false).find_by_username_or_email(normalized_login_param)
end
if user
raise Discourse::ReadOnly if staff_writes_only_mode? && !user.staff?
enqueue_password_reset_for_user(user)
else
RateLimiter.new(
nil,
"forgot-password-login-hour-#{normalized_login_param}",
5,
1.hour,
).performed!
end
json = success_json
json[:user_found] = user.present? if !SiteSetting.hide_email_address_taken
render json: json
rescue RateLimiter::LimitExceeded
render_json_error(I18n.t("rate_limiter.slow_down"))
end
|
#get_honeypot_value ⇒ Object
694
695
696
697
698
699
700
701
702
703
|
# File 'app/controllers/session_controller.rb', line 694
def get_honeypot_value
secure_session.set(HONEYPOT_KEY, honeypot_value, expires: 1.hour)
secure_session.set(CHALLENGE_KEY, challenge_value, expires: 1.hour)
render json: {
value: honeypot_value,
challenge: challenge_value,
expires_in: SecureSession.expiry,
}
end
|
#login_sso_user(sso, user) ⇒ Object
317
318
319
320
321
322
|
# File 'app/controllers/session_controller.rb', line 317
def login_sso_user(sso, user)
connect_verbose_warn do
"Verbose SSO log: User was logged on #{user.username}\n\n#{sso.diagnostics}"
end
log_on_user(user) if user.id != current_user&.id
end
|
#one_time_password ⇒ Object
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
|
# File 'app/controllers/session_controller.rb', line 473
def one_time_password
@otp_username = otp_username = Discourse.redis.get "otp_#{params[:token]}"
if otp_username && user = User.find_by_username(otp_username)
if current_user&.username == otp_username
Discourse.redis.del "otp_#{params[:token]}"
return redirect_to path("/")
elsif request.post?
log_on_user(user)
Discourse.redis.del "otp_#{params[:token]}"
return redirect_to path("/")
else
end
else
@error = I18n.t("user_api_key.invalid_token")
end
render layout: "no_ember", locals: { hide_auth_buttons: true }
end
|
#passkey_challenge ⇒ Object
379
380
381
|
# File 'app/controllers/session_controller.rb', line 379
def passkey_challenge
render json: DiscourseWebauthn.stage_challenge(current_user, secure_session)
end
|
#passkey_login ⇒ Object
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
|
# File 'app/controllers/session_controller.rb', line 383
def passkey_login
raise Discourse::NotFound unless SiteSetting.enable_passkeys
params.require(:publicKeyCredential)
security_key =
::DiscourseWebauthn::AuthenticationService.new(
nil,
params[:publicKeyCredential],
session: secure_session,
factor_type: UserSecurityKey.factor_types[:first_factor],
).authenticate_security_key
user = User.where(id: security_key.user_id, active: true).first
if user.email_confirmed?
login(user, passkey_login: true)
else
not_activated(user)
end
rescue ::DiscourseWebauthn::SecurityKeyError => err
render_json_error(err.message, status: 401)
end
|
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
|
# File 'app/controllers/session_controller.rb', line 542
def second_factor_auth_perform
nonce = params.require(:nonce)
challenge = nil
error_key = nil
user = nil
status_code = 200
begin
challenge =
SecondFactor::AuthManager.find_second_factor_challenge(
nonce: nonce,
secure_session: secure_session,
target_user: current_user,
)
user = User.find(challenge[:target_user_id])
rescue SecondFactor::BadChallenge => exception
error_key = exception.error_translation_key
status_code = exception.status_code
end
if error_key
json =
failed_json.merge(
ok: false,
error: I18n.t(error_key),
reason: "challenge_not_found_or_expired",
)
render json: failed_json.merge(json), status: status_code
return
end
second_factor_method = params[:second_factor_method].to_i
if !user.valid_second_factor_method_for_user?(second_factor_method)
raise Discourse::InvalidAccess.new
end
if !challenge[:allowed_methods].include?(second_factor_method)
raise Discourse::InvalidAccess.new
end
if !challenge[:successful]
rate_limit_second_factor!(user)
second_factor_auth_result = user.authenticate_second_factor(params, secure_session)
if second_factor_auth_result.ok
challenge[:successful] = true
challenge[:generated_at] += 1.minute.to_i
secure_session["current_second_factor_auth_challenge"] = challenge.to_json
else
error_json =
second_factor_auth_result
.to_h
.deep_symbolize_keys
.slice(:ok, :error, :reason)
.merge(failed_json)
render json: error_json, status: 400
return
end
end
render json: {
ok: true,
callback_method: challenge[:callback_method],
callback_path: challenge[:callback_path],
redirect_url: challenge[:redirect_url],
},
status: 200
end
|
#second_factor_auth_show ⇒ Object
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
|
# File 'app/controllers/session_controller.rb', line 494
def second_factor_auth_show
nonce = params.require(:nonce)
challenge = nil
error_key = nil
user = nil
status_code = 200
begin
challenge =
SecondFactor::AuthManager.find_second_factor_challenge(
nonce: nonce,
secure_session: secure_session,
target_user: current_user,
)
rescue SecondFactor::BadChallenge => exception
error_key = exception.error_translation_key
status_code = exception.status_code
end
json = {}
if challenge
user = User.find(challenge[:target_user_id])
json.merge!(
totp_enabled: user.totp_enabled?,
backup_enabled: user.backup_codes_enabled?,
allowed_methods: challenge[:allowed_methods],
)
if user.security_keys_enabled?
DiscourseWebauthn.stage_challenge(user, secure_session)
json.merge!(DiscourseWebauthn.allowed_credentials(user, secure_session))
json[:security_keys_enabled] = true
else
json[:security_keys_enabled] = false
end
json[:description] = challenge[:description] if challenge[:description]
else
json[:error] = I18n.t(error_key)
end
respond_to do |format|
format.html do
store_preloaded("2fa_challenge_data", MultiJson.dump(json))
raise ApplicationController::RenderEmpty.new
end
format.json { render json: json, status: status_code }
end
end
|
#sso ⇒ Object
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
# File 'app/controllers/session_controller.rb', line 24
def sso
raise Discourse::NotFound unless SiteSetting.enable_discourse_connect?
destination_url = cookies[:destination_url] || session[:destination_url]
return_path = params[:return_path] || path("/")
if destination_url && return_path == path("/")
uri = URI.parse(destination_url)
return_path = "#{uri.path}#{uri.query ? "?#{uri.query}" : ""}"
end
session.delete(:destination_url)
cookies.delete(:destination_url)
sso = DiscourseConnect.generate_sso(return_path, secure_session: secure_session)
connect_verbose_warn { "Verbose SSO log: Started SSO process\n\n#{sso.diagnostics}" }
redirect_to sso_url(sso), allow_other_host: true
end
|
#sso_login ⇒ Object
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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
|
# File 'app/controllers/session_controller.rb', line 164
def sso_login
raise Discourse::NotFound unless SiteSetting.enable_discourse_connect
raise Discourse::ReadOnly if @readonly_mode && !staff_writes_only_mode?
params.require(:sso)
params.require(:sig)
begin
sso = DiscourseConnect.parse(request.query_string, secure_session: secure_session)
rescue DiscourseConnect::PayloadParseError => e
connect_verbose_warn do
"Verbose SSO log: Payload is not base64\n\n#{e.message}\n\n#{sso&.diagnostics}"
end
return render_sso_error(text: I18n.t("discourse_connect.payload_parse_error"), status: 422)
rescue DiscourseConnect::SignatureError => e
connect_verbose_warn do
"Verbose SSO log: Signature verification failed\n\n#{e.message}\n\n#{sso&.diagnostics}"
end
return render_sso_error(text: I18n.t("discourse_connect.signature_error"), status: 422)
end
if !sso.nonce_valid?
connect_verbose_warn { "Verbose SSO log: #{sso.nonce_error}\n\n#{sso.diagnostics}" }
return render_sso_error(text: I18n.t("discourse_connect.timeout_expired"), status: 419)
end
if ScreenedIpAddress.should_block?(request.remote_ip)
connect_verbose_warn do
"Verbose SSO log: IP address is blocked #{request.remote_ip}\n\n#{sso.diagnostics}"
end
return render_sso_error(text: I18n.t("discourse_connect.unknown_error"), status: 500)
end
return_path = sso.return_path
sso.expire_nonce!
begin
invite = validate_invitiation!(sso)
if user = sso.lookup_or_create_user(request.remote_ip)
raise Discourse::ReadOnly if staff_writes_only_mode? && !user&.staff?
if user.suspended?
render_sso_error(text: failed_to_login(user)[:error], status: 403)
return
end
if SiteSetting.must_approve_users? && !user.approved?
redeem_invitation(invite, sso, user) if invite.present? && user.invited_user.blank?
if SiteSetting.discourse_connect_not_approved_url.present?
redirect_to SiteSetting.discourse_connect_not_approved_url, allow_other_host: true
else
render_sso_error(text: I18n.t("discourse_connect.account_not_approved"), status: 403)
end
return
elsif invite.present? && user.invited_user.blank?
redeem_invitation(invite, sso, user)
user.activate
login_sso_user(sso, user)
topic = invite.topics.first
return_path = topic.present? ? path(topic.relative_url) : path("/")
elsif !user.active?
activation = UserActivator.new(user, request, session, cookies)
activation.finish
session["user_created_message"] = activation.message
return redirect_to(users_account_created_path)
else
login_sso_user(sso, user)
end
if return_path !~ %r{\A/[^/]}
begin
uri = URI(return_path)
if (uri.hostname == Discourse.current_hostname)
return_path = uri.to_s
elsif !domain_redirect_allowed?(uri.hostname)
return_path = path("/")
end
rescue StandardError
return_path = path("/")
end
end
return_path = path("/") if return_path.include?(path("/session/sso"))
redirect_to return_path, allow_other_host: true
else
render_sso_error(text: I18n.t("discourse_connect.not_found"), status: 500)
end
rescue ActiveRecord::RecordInvalid => e
connect_verbose_warn { <<~TEXT }
Verbose SSO log: Record was invalid: #{e.record.class.name} #{e.record.id}
#{e.record.errors.to_h}
Attributes:
#{e.record.attributes.slice(*DiscourseConnectBase::ACCESSORS.map(&:to_s))}
SSO Diagnostics:
#{sso.diagnostics}
TEXT
text = nil
if (e.record.is_a?(User) && e.record.errors[:primary_email].present?)
if e.record.email.blank?
text = I18n.t("discourse_connect.no_email")
else
text =
I18n.t("discourse_connect.email_error", email: ERB::Util.html_escape(e.record.email))
end
end
render_sso_error(text: text || I18n.t("discourse_connect.unknown_error"), status: 500)
rescue DiscourseConnect::BlankExternalId
render_sso_error(text: I18n.t("discourse_connect.blank_id_error"), status: 500)
rescue Invite::ValidationFailed => e
render_sso_error(text: e.message, status: 400)
rescue Invite::RedemptionFailed => e
render_sso_error(text: I18n.t("discourse_connect.invite_redeem_failed"), status: 412)
rescue Invite::UserExists => e
render_sso_error(text: e.message, status: 412)
rescue => e
message = +"Failed to create or lookup user: #{e}."
message << " "
message << " #{sso.diagnostics}"
message << " "
message << " #{e.backtrace.join("\n")}"
Rails.logger.error(message)
render_sso_error(text: I18n.t("discourse_connect.unknown_error"), status: 500)
end
end
|
#sso_provider(payload = nil, confirmed_2fa_during_login = false) ⇒ Object
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
|
# File 'app/controllers/session_controller.rb', line 43
def sso_provider(payload = nil, confirmed_2fa_during_login = false)
raise Discourse::NotFound unless SiteSetting.enable_discourse_connect_provider
result =
run_second_factor!(
SecondFactor::Actions::DiscourseConnectProvider,
action_data: {
payload: payload,
confirmed_2fa_during_login: confirmed_2fa_during_login,
},
)
if result.second_factor_auth_skipped?
data = result.data
if data[:logout]
params[:return_url] = data[:return_sso_url]
destroy
return
end
if data[:no_current_user]
if data[:prompt] == "none"
redirect_to data[:sso_redirect_url], allow_other_host: true
return
else
cookies[:sso_payload] = payload || request.query_string
redirect_to path("/login")
return
end
end
if request.xhr?
cookies[:sso_destination_url] = data[:sso_redirect_url]
render json: success_json.merge(redirect_url: data[:sso_redirect_url])
else
redirect_to data[:sso_redirect_url], allow_other_host: true
end
elsif result.no_second_factors_enabled?
if request.xhr?
cookies[:sso_destination_url] = result.data[:sso_redirect_url]
else
redirect_to result.data[:sso_redirect_url], allow_other_host: true
end
elsif result.second_factor_auth_completed?
redirect_url = result.data[:sso_redirect_url]
render json: success_json.merge(redirect_url: redirect_url)
end
rescue DiscourseConnectProvider::BlankSecret
render plain: I18n.t("discourse_connect.missing_secret"), status: 400
rescue DiscourseConnectProvider::ParseError
render plain: I18n.t("discourse_connect.login_error"), status: 422
rescue DiscourseConnectProvider::BlankReturnUrl
render plain: "return_sso_url is blank, it must be provided", status: 400
rescue DiscourseConnectProvider::InvalidParameterValueError => e
render plain: I18n.t("discourse_connect.invalid_parameter_value", param: e.param), status: 400
end
|
#test_second_factor_restricted_route ⇒ Object
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
|
# File 'app/controllers/session_controller.rb', line 140
def test_second_factor_restricted_route
target_user = User.find_by_username(params[:username]) || current_user
raise "user required" if !target_user
result =
run_second_factor!(TestSecondFactorAction, target_user: target_user) do |manager|
manager.allow_backup_codes! if params[:allow_backup_codes]
end
if result.no_second_factors_enabled?
render json: { result: "no_second_factors_enabled" }
else
render json: { result: "second_factor_auth_completed" }
end
rescue StandardError => e
if e.message == "running 2fa against another user is not allowed"
render json: { result: "wrong user" }, status: 400
else
raise e
end
end
|