discourse/lib/auth/google_oauth2_authenticator.rb

132 lines
3.8 KiB
Ruby

class Auth::GoogleOAuth2Authenticator < Auth::Authenticator
def name
"google_oauth2"
end
def enabled?
SiteSetting.enable_google_oauth2_logins
end
def description_for_user(user)
info = GoogleUserInfo.find_by(user_id: user.id)
info&.email || info&.name || ""
end
def can_revoke?
true
end
def revoke(user, skip_remote: false)
info = GoogleUserInfo.find_by(user_id: user.id)
raise Discourse::NotFound if info.nil?
# We get a temporary token from google upon login but do not need it, and do not store it.
# Therefore we do not have any way to revoke the token automatically on google's end
info.destroy!
true
end
def can_connect_existing_user?
true
end
def after_authenticate(auth_hash, existing_account: nil)
session_info = parse_hash(auth_hash)
google_hash = session_info[:google]
result = ::Auth::Result.new
result.email = session_info[:email]
result.email_valid = session_info[:email_valid]
result.name = session_info[:name]
result.extra_data = google_hash
user_info = ::GoogleUserInfo.find_by(google_user_id: google_hash[:google_user_id])
if existing_account && (user_info.nil? || existing_account.id != user_info.user_id)
user_info.destroy! if user_info
result.user = existing_account
user_info = GoogleUserInfo.create!({ user_id: result.user.id }.merge(google_hash))
else
result.user = user_info&.user
end
if !result.user && !result.email.blank? && result.email_valid
result.user = User.find_by_email(result.email)
if result.user
# we've matched an existing user to this login attempt...
if result.user.google_user_info && result.user.google_user_info.google_user_id != google_hash[:google_user_id]
# but the user has changed the google account used to log in...
if result.user.google_user_info.email != google_hash[:email]
# the user changed their email, go ahead and scrub the old record
result.user.google_user_info.destroy!
else
# same email address but different account? likely a takeover scenario
result.failed = true
result.failed_reason = I18n.t('errors.conflicting_google_user_id')
return result
end
end
::GoogleUserInfo.create({ user_id: result.user.id }.merge(google_hash))
end
end
result
end
def after_create_account(user, auth)
data = auth[:extra_data]
GoogleUserInfo.create({ user_id: user.id }.merge(data))
end
def register_middleware(omniauth)
options = {
setup: lambda { |env|
strategy = env["omniauth.strategy"]
strategy.options[:client_id] = SiteSetting.google_oauth2_client_id
strategy.options[:client_secret] = SiteSetting.google_oauth2_client_secret
},
skip_jwt: true
}
if (google_oauth2_prompt = SiteSetting.google_oauth2_prompt).present?
options[:prompt] = google_oauth2_prompt.gsub("|", " ")
end
google_oauth2_hd = SiteSetting.google_oauth2_hd
options[:hd] = google_oauth2_hd if google_oauth2_hd.present?
# jwt encoding is causing auth to fail in quite a few conditions
# skipping
omniauth.provider :google_oauth2, options
end
protected
def parse_hash(hash)
extra = hash[:extra][:raw_info]
h = {}
h[:email] = hash[:info][:email]
h[:name] = hash[:info][:name]
h[:email_valid] = extra[:email_verified]
h[:google] = {
google_user_id: hash[:uid] || extra[:sub],
email: extra[:email],
first_name: extra[:given_name],
last_name: extra[:family_name],
gender: extra[:gender],
name: extra[:name],
link: extra[:hd],
profile_link: extra[:profile],
picture: extra[:picture]
}
h
end
end