Add Google Oauth2 authenticator. The current Google OpenID authentication has been deprecated by Google and will NOT work for any new websites.

This commit is contained in:
Neil Lalonde 2014-05-21 18:19:40 -04:00
parent ca95bdb023
commit 742841ddce
15 changed files with 204 additions and 4 deletions

View File

@ -129,6 +129,7 @@ gem 'omniauth-facebook'
gem 'omniauth-twitter' gem 'omniauth-twitter'
gem 'omniauth-github' gem 'omniauth-github'
gem 'omniauth-oauth2', require: false gem 'omniauth-oauth2', require: false
gem 'omniauth-google-oauth2'
gem 'oj' gem 'oj'
# while resolving https://groups.google.com/forum/#!topic/ruby-pg/5_ylGmog1S4 # while resolving https://groups.google.com/forum/#!topic/ruby-pg/5_ylGmog1S4
gem 'pg', '0.15.1' gem 'pg', '0.15.1'

View File

@ -197,6 +197,9 @@ GEM
omniauth-github (1.1.1) omniauth-github (1.1.1)
omniauth (~> 1.0) omniauth (~> 1.0)
omniauth-oauth2 (~> 1.1) omniauth-oauth2 (~> 1.1)
omniauth-google-oauth2 (0.2.4)
omniauth (~> 1.0)
omniauth-oauth2 (~> 1.1)
omniauth-oauth (1.0.1) omniauth-oauth (1.0.1)
oauth oauth
omniauth (~> 1.0) omniauth (~> 1.0)
@ -436,6 +439,7 @@ DEPENDENCIES
omniauth omniauth
omniauth-facebook omniauth-facebook
omniauth-github omniauth-github
omniauth-google-oauth2
omniauth-oauth2 omniauth-oauth2
omniauth-openid omniauth-openid
omniauth-twitter omniauth-twitter

View File

@ -31,6 +31,7 @@ Discourse.LoginMethod.reopenClass({
* */ * */
[ "google", [ "google",
"google_oauth2",
"facebook", "facebook",
"cas", "cas",
"twitter", "twitter",
@ -41,7 +42,7 @@ Discourse.LoginMethod.reopenClass({
var params = {name: name}; var params = {name: name};
if (name === "google") { if (name === "google" || name === "google_oauth2") {
params.frameWidth = 850; params.frameWidth = 850;
params.frameHeight = 500; params.frameHeight = 500;
} else if (name === "facebook") { } else if (name === "facebook") {

View File

@ -125,7 +125,7 @@
font-family: zocial; font-family: zocial;
line-height: 0.9; line-height: 0.9;
} }
&.google { &.google, &.google_oauth2 {
background: $google; background: $google;
&:before { &:before {
content: "G"; content: "G";

View File

@ -8,6 +8,7 @@ class Users::OmniauthCallbacksController < ApplicationController
BUILTIN_AUTH = [ BUILTIN_AUTH = [
Auth::FacebookAuthenticator.new, Auth::FacebookAuthenticator.new,
Auth::OpenIdAuthenticator.new("google", "https://www.google.com/accounts/o8/id", trusted: true), Auth::OpenIdAuthenticator.new("google", "https://www.google.com/accounts/o8/id", trusted: true),
Auth::GoogleOAuth2Authenticator.new,
Auth::OpenIdAuthenticator.new("yahoo", "https://me.yahoo.com", trusted: true), Auth::OpenIdAuthenticator.new("yahoo", "https://me.yahoo.com", trusted: true),
Auth::GithubAuthenticator.new, Auth::GithubAuthenticator.new,
Auth::TwitterAuthenticator.new Auth::TwitterAuthenticator.new

View File

@ -27,6 +27,9 @@ class AdminDashboardData
gc_checks, gc_checks,
sidekiq_check || queue_size_check, sidekiq_check || queue_size_check,
ram_check, ram_check,
old_google_config_check,
both_googles_config_check,
google_oauth2_config_check,
facebook_config_check, facebook_config_check,
twitter_config_check, twitter_config_check,
github_config_check, github_config_check,
@ -45,6 +48,7 @@ class AdminDashboardData
].compact ].compact
end end
def self.fetch_stats def self.fetch_stats
AdminDashboardData.new AdminDashboardData.new
end end
@ -106,8 +110,20 @@ class AdminDashboardData
I18n.t('dashboard.memory_warning') if MemInfo.new.mem_total and MemInfo.new.mem_total < 1_000_000 I18n.t('dashboard.memory_warning') if MemInfo.new.mem_total and MemInfo.new.mem_total < 1_000_000
end end
def old_google_config_check
I18n.t('dashboard.enable_google_logins_warning') if SiteSetting.enable_google_logins
end
def both_googles_config_check
I18n.t('dashboard.both_googles_warning') if SiteSetting.enable_google_logins && SiteSetting.enable_google_oauth2_logins
end
def google_oauth2_config_check
I18n.t('dashboard.google_oauth2_config_warning') if SiteSetting.enable_google_logins && (SiteSetting.google_oauth2_client_id.blank? || SiteSetting.google_oauth2_client_secret.blank?)
end
def facebook_config_check def facebook_config_check
I18n.t('dashboard.facebook_config_warning') if SiteSetting.enable_facebook_logins and (SiteSetting.facebook_app_id.blank? or SiteSetting.facebook_app_secret.blank?) I18n.t('dashboard.facebook_config_warning') if SiteSetting.enable_facebook_logins && (SiteSetting.facebook_app_id.blank? || SiteSetting.facebook_app_secret.blank?)
end end
def twitter_config_check def twitter_config_check

View File

@ -0,0 +1,3 @@
class GoogleUserInfo < ActiveRecord::Base
belongs_to :user
end

View File

@ -509,6 +509,9 @@ en:
google: google:
title: "with Google" title: "with Google"
message: "Authenticating with Google (make sure pop up blockers are not enabled)" message: "Authenticating with Google (make sure pop up blockers are not enabled)"
google_oauth2:
title: "with Google"
message: "Authenticating with Google (make sure pop up blockers are not enabled)"
twitter: twitter:
title: "with Twitter" title: "with Twitter"
message: "Authenticating with Twitter (make sure pop up blockers are not enabled)" message: "Authenticating with Twitter (make sure pop up blockers are not enabled)"

View File

@ -518,6 +518,9 @@ en:
sidekiq_warning: 'Sidekiq is not running. Many tasks, like sending emails, are executed asynchronously by sidekiq. Please ensure at least one sidekiq process is running. <a href="https://github.com/mperham/sidekiq" target="_blank">Learn about Sidekiq here</a>.' sidekiq_warning: 'Sidekiq is not running. Many tasks, like sending emails, are executed asynchronously by sidekiq. Please ensure at least one sidekiq process is running. <a href="https://github.com/mperham/sidekiq" target="_blank">Learn about Sidekiq here</a>.'
queue_size_warning: 'The number of queued jobs is %{queue_size}, which is high. This could indicate a problem with the Sidekiq process(es), or you may need to add more Sidekiq workers.' queue_size_warning: 'The number of queued jobs is %{queue_size}, which is high. This could indicate a problem with the Sidekiq process(es), or you may need to add more Sidekiq workers.'
memory_warning: 'Your server is running with less than 1 GB of total memory. At least 1 GB of memory is recommended.' memory_warning: 'Your server is running with less than 1 GB of total memory. At least 1 GB of memory is recommended.'
enable_google_logins_warning: "You are using a deprecated version of Google's OpenID authentication. Google will be ending support for OpenID by April 20, 2015. Start using Google Oauth2 as soon as possible."
both_googles_warning: "You have both enable_google_logins and enable_google_oauth2_logins checked in the site settings. Disable enable_google_logins."
google_oauth2_config_warning: 'The server is configured to allow signup and log in with Google Oauth2 (enable_google_oauth2_logins), but the client id and client secret values are not set. Go to <a href="/admin/site_settings">the Site Settings</a> and update the settings.'
facebook_config_warning: 'The server is configured to allow signup and log in with Facebook (enable_facebook_logins), but the app id and app secret values are not set. Go to <a href="/admin/site_settings">the Site Settings</a> and update the settings. <a href="https://meta.discourse.org/t/configuring-facebook-login-for-discourse/13394" target="_blank">See this guide to learn more</a>.' facebook_config_warning: 'The server is configured to allow signup and log in with Facebook (enable_facebook_logins), but the app id and app secret values are not set. Go to <a href="/admin/site_settings">the Site Settings</a> and update the settings. <a href="https://meta.discourse.org/t/configuring-facebook-login-for-discourse/13394" target="_blank">See this guide to learn more</a>.'
twitter_config_warning: 'The server is configured to allow signup and log in with Twitter (enable_twitter_logins), but the key and secret values are not set. Go to <a href="/admin/site_settings">the Site Settings</a> and update the settings. <a href="https://meta.discourse.org/t/configuring-twitter-login-for-discourse/13395" target="_blank">See this guide to learn more</a>.' twitter_config_warning: 'The server is configured to allow signup and log in with Twitter (enable_twitter_logins), but the key and secret values are not set. Go to <a href="/admin/site_settings">the Site Settings</a> and update the settings. <a href="https://meta.discourse.org/t/configuring-twitter-login-for-discourse/13395" target="_blank">See this guide to learn more</a>.'
github_config_warning: 'The server is configured to allow signup and log in with GitHub (enable_github_logins), but the client id and secret values are not set. Go to <a href="/admin/site_settings">the Site Settings</a> and update the settings. <a href="https://meta.discourse.org/t/configuring-github-login-for-discourse/13745" target="_blank">See this guide to learn more</a>.' github_config_warning: 'The server is configured to allow signup and log in with GitHub (enable_github_logins), but the client id and secret values are not set. Go to <a href="/admin/site_settings">the Site Settings</a> and update the settings. <a href="https://meta.discourse.org/t/configuring-github-login-for-discourse/13745" target="_blank">See this guide to learn more</a>.'
@ -720,9 +723,13 @@ en:
sso_overrides_name: "Overrides local name with external site name from SSO payload (WARNING: discrepancies can occur due to normalization of local names)" sso_overrides_name: "Overrides local name with external site name from SSO payload (WARNING: discrepancies can occur due to normalization of local names)"
enable_local_logins: "Enable traditional, local username and password authentication" enable_local_logins: "Enable traditional, local username and password authentication"
enable_google_logins: "Enable Google authentication" enable_google_logins: "(deprecated) Enable Google authentication. This is the OpenID method of authentication which Google has deprecated. New installs will NOT work with this. Use Google Oauth2 instead. Existing installs must move to Google Oauth2 by April 20, 2015."
enable_yahoo_logins: "Enable Yahoo authentication" enable_yahoo_logins: "Enable Yahoo authentication"
enable_google_oauth2_logins: "Enable Google Oauth2 authentication. This is the method of authentication that Google currently supports. Requires key and secret."
google_oauth2_client_id: "Client ID of your Google application."
google_oauth2_client_secret: "Client secret of your Google application."
enable_twitter_logins: "Enable Twitter authentication, requires twitter_consumer_key and twitter_consumer_secret" enable_twitter_logins: "Enable Twitter authentication, requires twitter_consumer_key and twitter_consumer_secret"
twitter_consumer_key: "Consumer key for Twitter authentication, registered at http://dev.twitter.com" twitter_consumer_key: "Consumer key for Twitter authentication, registered at http://dev.twitter.com"
twitter_consumer_secret: "Consumer secret for Twitter authentication, registered at http://dev.twitter.com" twitter_consumer_secret: "Consumer secret for Twitter authentication, registered at http://dev.twitter.com"

View File

@ -114,8 +114,13 @@ users:
default: 8 default: 8
block_common_passwords: true block_common_passwords: true
enable_google_logins: enable_google_logins:
client: true
default: false
enable_google_oauth2_logins:
client: true client: true
default: true default: true
google_oauth2_client_id: ''
google_oauth2_client_secret: ''
enable_yahoo_logins: enable_yahoo_logins:
client: true client: true
default: true default: true

View File

@ -0,0 +1,20 @@
class CreateGoogleUserInfos < ActiveRecord::Migration
def change
create_table :google_user_infos do |t|
t.integer :user_id, null: false
t.string :google_user_id, null: false
t.string :first_name
t.string :last_name
t.string :email
t.string :gender
t.string :name
t.string :link
t.string :profile_link
t.string :picture
t.timestamps
end
add_index :google_user_infos, :user_id, unique: true
add_index :google_user_infos, :google_user_id, unique: true
end
end

View File

@ -0,0 +1,13 @@
class GoogleOpenidDefaultHasChanged < ActiveRecord::Migration
def up
result = Category.exec_sql("SELECT count(*) FROM site_settings WHERE name = 'enable_google_logins'")
if result[0]['count'].to_i == 0
# The old default was true, so add a row to keep it that way.
execute "INSERT INTO site_settings (name, data_type, value, created_at, updated_at) VALUES ('enable_google_logins', 5, 't', now(), now())"
end
end
def down
# No need to undo.
end
end

View File

@ -6,3 +6,4 @@ require_dependency 'auth/facebook_authenticator'
require_dependency 'auth/open_id_authenticator' require_dependency 'auth/open_id_authenticator'
require_dependency 'auth/github_authenticator' require_dependency 'auth/github_authenticator'
require_dependency 'auth/twitter_authenticator' require_dependency 'auth/twitter_authenticator'
require_dependency 'auth/google_oauth2_authenticator'

View File

@ -0,0 +1,67 @@
class Auth::GoogleOAuth2Authenticator < Auth::Authenticator
def name
"google_oauth2"
end
def after_authenticate(auth_hash)
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])
result.user = user_info.try(:user)
if !result.user && !result.email.blank? && result.user = User.find_by(email: Email.downcase(result.email))
GoogleUserInfo.create({user_id: result.user.id}.merge(google_hash))
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)
omniauth.provider :google_oauth2,
: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
}
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] = hash[:extra][:raw_info][: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

View File

@ -0,0 +1,58 @@
require 'spec_helper'
# For autospec:
Auth.send(:remove_const, :GoogleOAuth2Authenticator)
load 'auth/google_oauth2_authenticator.rb'
describe Auth::GoogleOAuth2Authenticator do
context 'after_authenticate' do
it 'can authenticate and create a user record for already existing users' do
authenticator = described_class.new
user = Fabricate(:user)
hash = {
:uid => "123456789",
:info => {
:name => "John Doe",
:email => user.email
},
:extra => {
:raw_info => {
:email => "user@domain.example.com",
:email_verified => true,
:name => "John Doe"
}
}
}
result = authenticator.after_authenticate(hash)
result.user.id.should == user.id
end
it 'can create a proper result for non existing users' do
hash = {
:uid => "123456789",
:info => {
:name => "Jane Doe",
:email => "jane.doe@the.google.com"
},
:extra => {
:raw_info => {
:email => "jane.doe@the.google.com",
:email_verified => true,
:name => "Jane Doe"
}
}
}
authenticator = described_class.new
result = authenticator.after_authenticate(hash)
result.user.should be_nil
result.extra_data[:name].should == "Jane Doe"
end
end
end