Add support for Google OAuth2 authentication
authorTom Hughes <tom@compton.nu>
Sat, 28 Feb 2015 15:56:41 +0000 (15:56 +0000)
committerTom Hughes <tom@compton.nu>
Tue, 14 Apr 2015 09:08:07 +0000 (10:08 +0100)
This replaces OpenID authentication, which is going away soon, but
provides an upgrade path where we can migrate users that already have
a Google OpenID setup to the new system transparently.

Gemfile
Gemfile.lock
app/controllers/user_controller.rb
app/helpers/user_helper.rb
app/views/user/account.html.erb
app/views/user/login.html.erb
app/views/user/new.html.erb
config/example.application.yml
config/initializers/omniauth.rb
lib/auth.rb [new file with mode: 0644]

diff --git a/Gemfile b/Gemfile
index 5db975f81600c3bbd4174400b5307a6da28de6e7..7c916b01189a8763f817d4222d3d291248855d62 100644 (file)
--- a/Gemfile
+++ b/Gemfile
@@ -57,6 +57,7 @@ gem "actionpack-page_caching"
 # Omniauth for authentication
 gem "omniauth"
 gem "omniauth-openid"
+gem "openstreetmap-omniauth-google-oauth2", ">= 0.2.6.1", :require => "omniauth-google-oauth2"
 
 # Markdown formatting support
 gem "redcarpet"
index aa53ba8ba4c8b10c3619e2c855b2a815024355be..8940c1fdf18e337ea37fd1c201957a924d059e39 100644 (file)
@@ -150,11 +150,21 @@ GEM
     omniauth (1.2.2)
       hashie (>= 1.2, < 4)
       rack (~> 1.0)
+    omniauth-oauth2 (1.2.0)
+      faraday (>= 0.8, < 0.10)
+      multi_json (~> 1.3)
+      oauth2 (~> 1.0)
+      omniauth (~> 1.2)
     omniauth-openid (1.0.1)
       omniauth (~> 1.0)
       rack-openid (~> 1.3.1)
     openstreetmap-i18n-js (3.0.0.rc5.3)
       i18n
+    openstreetmap-omniauth-google-oauth2 (0.2.6.1)
+      jwt (~> 1.0)
+      multi_json (~> 1.3)
+      omniauth (>= 1.1.1)
+      omniauth-oauth2 (>= 1.1.1)
     paperclip (4.2.1)
       activemodel (>= 3.0.0)
       activesupport (>= 3.0.0)
@@ -305,6 +315,7 @@ DEPENDENCIES
   omniauth
   omniauth-openid
   openstreetmap-i18n-js (>= 3.0.0.rc5.3)
+  openstreetmap-omniauth-google-oauth2 (>= 0.2.6.1)
   paperclip (~> 4.0)
   pg
   poltergeist
index 367c0471dc1d5b3f60a2fcb03fa05de1bf8e8c71..c7e44dab64e3da5f4f541dff74fdf07eff87e2e5 100644 (file)
@@ -227,7 +227,7 @@ class UserController < ApplicationController
 
       @user.status = "pending"
 
-      if @user.auth_provider.present? && @user.auth_uid.present? && @user.pass_crypt.empty?
+      if @user.auth_provider.present? && @user.pass_crypt.empty?
         # We are creating an account with external authentication and
         # no password was specified so create a random one
         @user.pass_crypt = SecureRandom.base64(16)
@@ -237,7 +237,7 @@ class UserController < ApplicationController
       if @user.invalid?
         # Something is wrong with a new user, so rerender the form
         render :action => "new"
-      elsif @user.auth_provider.present? && @user.auth_uid.present?
+      elsif @user.auth_provider.present?
         # Verify external authenticator before moving on
         session[:new_user] = @user
         redirect_to auth_url(@user.auth_provider, @user.auth_uid)
@@ -250,9 +250,9 @@ class UserController < ApplicationController
   end
 
   def login
-    if params[:username] || params[:openid_url]
-      session[:referer] ||= params[:referer]
+    session[:referer] = params[:referer] if params[:referer]
 
+    if params[:username] || params[:openid_url]
       if params[:openid_url].present?
         session[:remember_me] ||= params[:remember_me_openid]
         redirect_to auth_url("openid", params[:openid_url])
@@ -496,11 +496,21 @@ class UserController < ApplicationController
     when "openid"
       email_verified = uid.match(%r{https://www.google.com/accounts/o8/id?(.*)}) ||
                        uid.match(%r{https://me.yahoo.com/(.*)})
+    when "google"
+      email_verified = true
     else
       email_verified = false
     end
 
-    if user = User.find_by_auth_provider_and_auth_uid(provider, uid)
+    user = User.find_by_auth_provider_and_auth_uid(provider, uid)
+
+    if user.nil? && provider == "google"
+      openid_url = auth_info[:extra][:id_info]["openid_id"]
+      user = User.find_by_auth_provider_and_auth_uid("openid", openid_url) if openid_url
+      user.update(:auth_provider => provider, :auth_uid => uid) if user
+    end
+
+    if user
       case user.status
       when "pending" then
         unconfirmed_login(user)
@@ -668,8 +678,7 @@ class UserController < ApplicationController
       user.preferred_editor = params[:user][:preferred_editor]
     end
 
-    if params[:user][:auth_provider].nil? || params[:user][:auth_provider].blank? ||
-       params[:user][:auth_uid].nil? || params[:user][:auth_uid].blank?
+    if params[:user][:auth_provider].nil? || params[:user][:auth_provider].blank?
       user.auth_provider = nil
       user.auth_uid = nil
     end
index dd4c343d8d96cafcd1919432fc6318fb85d5ac7f..6043edf5f3e527b3e3ac0c563cdaaa5a4c7fb0bf 100644 (file)
@@ -48,7 +48,7 @@ module UserHelper
     image_tag "openid_small.png", :alt => t("user.login.openid_logo_alt"), :class => "openid_logo"
   end
 
-  def auth_button(name, provider, options)
+  def auth_button(name, provider, options = {})
     link_to(
       image_tag("#{name}.png", :alt => t("user.login.auth_providers.#{name}.alt")),
       auth_path(options.merge(:provider => provider)),
index 93ee79cb68e1f09b40ac715f14e455ddf9be9e3f..47a84e99bcbb86823f90ba45096889a3c453ba74 100644 (file)
@@ -48,7 +48,7 @@
   <fieldset>
     <div class="form-row">
       <label class="standard-label"><%= t 'user.account.external auth' %></label>
-      <%= f.select :auth_provider, { "None" => "", "OpenID" => "openid" } %>
+      <%= f.select :auth_provider, Auth::PROVIDERS %>
       <%= f.text_field :auth_uid %>
       <span class="form-help deemphasize">(<a href="<%= t 'user.account.openid.link' %>" target="_new"><%= t 'user.account.openid.link text' %></a>)</span>
     </diV>
index 8e2c3c33620f887e73d36d73ac6e219bd6bc564f..99d7c0c347e645707f195d2cadb1e01a372b5a78 100644 (file)
@@ -42,7 +42,9 @@
 
         <ul class='clearfix' id="login_auth_buttons">
           <li><%= link_to image_tag("openid.png", :alt => t("user.login.auth_providers.openid.title")), "#", :id => "openid_open_url", :title => t("user.login.auth_providers.openid.title") %></li>
-          <li><%= auth_button "google", "openid", :openid_url => "https://www.google.com/accounts/o8/id" %></li>
+          <% if defined?(GOOGLE_AUTH_ID) -%>
+          <li><%= auth_button "google", "google" %></li>
+          <% end -%>
           <li><%= auth_button "yahoo", "openid", :openid_url => "yahoo.com" %></li>
           <li><%= auth_button "wordpress", "openid", :openid_url => "wordpress.com" %></li>
           <li><%= auth_button "aol", "openid", :openid_url => "aol.com" %></li>
index 687857bfd709e93d84fdb49216305b97d827b2ad..fcd775a484eca5046710e344a2ecdc92dce5fe3c 100644 (file)
@@ -45,7 +45,7 @@
       <label for="openid_url" class="standard-label">
         <%= raw t 'user.new.external auth' %>
       </label>
-      <%= select(:user, :auth_provider, { "None" => "", "OpenID" => "openid" }, { :default => "", :tabindex => 4 }) %>
+      <%= select(:user, :auth_provider, Auth::PROVIDERS, { :default => "", :tabindex => 4 }) %>
       <%= text_field(:user, :auth_uid, { :tabindex => 5 }) %>
       <%= error_message_on(:user, :auth_uid) %>
     </div>
index bef0f2845b5cca3395ff3fb1893a9b525c121b90..bb556d2c99c2abaab4e6b5b864cec4a0aeb90bce 100644 (file)
@@ -88,6 +88,10 @@ defaults: &defaults
     - ".*\\.google\\.ru/.*"
   # URL of Overpass instance to use for feature queries
   overpass_url: "//overpass-api.de/api/interpreter"
+  # External authentication credentials
+  #google_auth_id: ""
+  #google_auth_secret: ""
+  #google_openid_realm: ""
 
 development:
   <<: *defaults
index ea174a83eb38e43f3e33bcc34f77d9c42fd6d813..6de63f6ee73430f8ff997261a9c8b26883d63cf4 100644 (file)
@@ -19,8 +19,16 @@ else
   openid_store = OpenID::Store::Filesystem.new(Rails.root.join("tmp/openids"))
 end
 
+openid_options = { :name => "openid", :store => openid_store }
+google_options = { :name => "google", :scope => "email", :access_type => "online" }
+
+if defined?(GOOGLE_OPENID_REALM)
+  google_options[:openid_realm] = GOOGLE_OPENID_REALM
+end
+
 Rails.application.config.middleware.use OmniAuth::Builder do
-  provider :openid, :name => "openid", :store => openid_store
+  provider :openid, openid_options
+  provider :google_oauth2, GOOGLE_AUTH_ID, GOOGLE_AUTH_SECRET, google_options if defined?(GOOGLE_AUTH_ID)
 end
 
 # Pending fix for: https://github.com/intridea/omniauth/pull/795
diff --git a/lib/auth.rb b/lib/auth.rb
new file mode 100644 (file)
index 0000000..edd98e4
--- /dev/null
@@ -0,0 +1,4 @@
+module Auth
+  PROVIDERS = { "None" => "", "OpenID" => "openid" }
+  PROVIDERS["Google"] = "google" if defined?(GOOGLE_AUTH_ID)
+end