-
Notifications
You must be signed in to change notification settings - Fork 8.3k
/
omniauth_callbacks_controller.rb
164 lines (133 loc) · 5.31 KB
/
omniauth_callbacks_controller.rb
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
# -*- encoding : utf-8 -*-
# frozen_string_literal: true
require_dependency 'email'
require_dependency 'enum'
require_dependency 'user_name_suggester'
class Users::OmniauthCallbacksController < ApplicationController
skip_before_action :redirect_to_login_if_required
layout 'no_ember'
# need to be able to call this
skip_before_action :check_xhr
# this is the only spot where we allow CSRF, our openid / oauth redirect
# will not have a CSRF token, however the payload is all validated so its safe
skip_before_action :verify_authenticity_token, only: :complete
def confirm_request
self.class.find_authenticator(params[:provider])
render locals: { hide_auth_buttons: true }
end
def complete
auth = request.env["omniauth.auth"]
raise Discourse::NotFound unless request.env["omniauth.auth"]
auth[:session] = session
authenticator = self.class.find_authenticator(params[:provider])
provider = DiscoursePluginRegistry.auth_providers.find { |p| p.name == params[:provider] }
if session.delete(:auth_reconnect) && authenticator.can_connect_existing_user? && current_user
# Save to redis, with a secret token, then redirect to confirmation screen
token = SecureRandom.hex
$redis.setex "#{Users::AssociateAccountsController::REDIS_PREFIX}_#{current_user.id}_#{token}", 10.minutes, auth.to_json
return redirect_to Discourse.base_uri("/associate/#{token}")
else
@auth_result = authenticator.after_authenticate(auth)
DiscourseEvent.trigger(:after_auth, authenticator, @auth_result)
end
preferred_origin = request.env['omniauth.origin']
if SiteSetting.enable_sso_provider && payload = cookies.delete(:sso_payload)
preferred_origin = session_sso_provider_url + "?" + payload
elsif cookies[:destination_url].present?
preferred_origin = cookies[:destination_url]
cookies.delete(:destination_url)
end
if preferred_origin.present?
parsed = begin
URI.parse(preferred_origin)
rescue URI::Error
end
if parsed && (parsed.host == nil || parsed.host == Discourse.current_hostname)
@origin = +"#{parsed.path}"
@origin << "?#{parsed.query}" if parsed.query
end
end
if @origin.blank?
@origin = Discourse.base_uri("/")
end
@auth_result.destination_url = @origin
if @auth_result.failed?
flash[:error] = @auth_result.failed_reason.html_safe
return render('failure')
else
@auth_result.authenticator_name = authenticator.name
complete_response_data
if provider&.full_screen_login || cookies['fsl']
cookies.delete('fsl')
cookies['_bypass_cache'] = true
cookies[:authentication_data] = @auth_result.to_client_hash.to_json
redirect_to @origin
else
respond_to do |format|
format.html
format.json { render json: @auth_result.to_client_hash }
end
end
end
end
def failure
flash[:error] = I18n.t("login.omniauth_error")
render 'failure'
end
def self.find_authenticator(name)
Discourse.enabled_authenticators.each do |authenticator|
return authenticator if authenticator.name == name
end
raise Discourse::InvalidAccess.new(I18n.t('authenticator_not_found'))
end
protected
def complete_response_data
if @auth_result.user
user_found(@auth_result.user)
elsif invite_required?
@auth_result.requires_invite = true
else
session[:authentication] = @auth_result.session_data
end
end
def user_found(user)
if user.totp_enabled?
@auth_result.omniauth_disallow_totp = true
@auth_result.email = user.email
return
end
# automatically activate/unstage any account if a provider marked the email valid
if @auth_result.email_valid && @auth_result.email == user.email
user.unstage
user.save
# ensure there is an active email token
unless EmailToken.where(email: user.email, confirmed: true).exists? ||
user.email_tokens.active.where(email: user.email).exists?
user.email_tokens.create!(email: user.email)
end
user.activate
user.update!(registration_ip_address: request.remote_ip) if user.registration_ip_address.blank?
end
if ScreenedIpAddress.should_block?(request.remote_ip)
@auth_result.not_allowed_from_ip_address = true
elsif ScreenedIpAddress.block_admin_login?(user, request.remote_ip)
@auth_result.admin_not_allowed_from_ip_address = true
elsif Guardian.new(user).can_access_forum? && user.active # log on any account that is active with forum access
log_on_user(user)
Invite.invalidate_for_email(user.email) # invite link can't be used to log in anymore
session[:authentication] = nil # don't carry around old auth info, perhaps move elsewhere
@auth_result.authenticated = true
else
if SiteSetting.must_approve_users? && !user.approved?
@auth_result.awaiting_approval = true
else
@auth_result.awaiting_activation = true
end
end
end
# If invite_only and enable_invite_only_oauth allow the user to authenticate if coming from the invite page
def invite_required?
(SiteSetting.invite_only? && !SiteSetting.enable_invite_only_oauth) ||
(SiteSetting.invite_only? && (!@origin.include?('invites') && SiteSetting.enable_invite_only_oauth))
end
end