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