adds omniauth basic authentication process with Twitter, including an intermediate step to ask the user for her email if not provided by the OAuth provider - Twitter, for instance

This commit is contained in:
David Gil
2015-08-24 19:44:46 +02:00
parent f0e47ee787
commit 158e203936
16 changed files with 167 additions and 19 deletions

View File

@@ -13,6 +13,8 @@ class ApplicationController < ActionController::Base
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_action :ensure_signup_complete
rescue_from CanCan::AccessDenied do |exception|
redirect_to main_app.root_url, alert: exception.message
end
@@ -40,4 +42,13 @@ class ApplicationController < ActionController::Base
def set_debate_votes(debates)
@voted_values = current_user ? current_user.debate_votes(debates) : {}
end
def ensure_signup_complete
# Ensure we don't go into an infinite loop
return if action_name.in? %w(finish_signup do_finish_signup)
if user_signed_in? && !current_user.email_provided?
redirect_to finish_signup_path
end
end
end

View File

@@ -0,0 +1,30 @@
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def self.provides_callback_for(provider)
class_eval %Q{
def #{provider}
@user = User.find_for_oauth(env["omniauth.auth"], current_user)
if @user.persisted?
sign_in_and_redirect @user, event: :authentication
set_flash_message(:notice, :success, kind: "#{provider}".capitalize) if is_navigational_format?
else
session["devise.#{provider}_data"] = env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
}
end
# [:twitter, :facebook, :google_oauth2].each do |provider|
[:twitter].each do |provider|
provides_callback_for provider
end
def after_sign_in_path_for(resource)
if resource.email_provided?
super(resource)
else
finish_signup_path
end
end
end

View File

@@ -1,4 +1,5 @@
class Users::RegistrationsController < Devise::RegistrationsController
prepend_before_filter :authenticate_scope!, only: [:edit, :update, :destroy, :finish_signup, :do_finish_signup]
def create
build_resource(sign_up_params)
@@ -9,6 +10,20 @@ class Users::RegistrationsController < Devise::RegistrationsController
end
end
def finish_signup
end
def do_finish_signup
if current_user.update(sign_up_params)
current_user.skip_reconfirmation!
sign_in(current_user, bypass: true)
redirect_to root_url, notice: I18n.t('devise.registrations.updated')
else
@show_errors = true
render :finish_signup
end
end
private
def sign_up_params

View File

@@ -1,8 +1,12 @@
class User < ActiveRecord::Base
include ActsAsParanoidAliases
OMNIAUTH_EMAIL_PREFIX = 'omniauth@participacion'
OMNIAUTH_EMAIL_REGEX = /\A#{OMNIAUTH_EMAIL_PREFIX}/
apply_simple_captcha
devise :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable
:recoverable, :rememberable, :trackable, :validatable, :omniauthable
acts_as_voter
acts_as_paranoid column: :hidden_at
@@ -11,9 +15,11 @@ class User < ActiveRecord::Base
has_one :moderator
has_one :organization
has_many :inappropiate_flags
has_many :identities, dependent: :destroy
validates :username, presence: true, unless: :organization?
validates :username, presence: true, unless: :organization?
validates :official_level, inclusion: {in: 0..5}
validates_format_of :email, without: OMNIAUTH_EMAIL_REGEX, on: :update
validates_associated :organization, message: false
@@ -25,6 +31,47 @@ class User < ActiveRecord::Base
scope :organizations, -> { joins(:organization) }
scope :officials, -> { where("official_level > 0") }
def self.find_for_oauth(auth, signed_in_resource = nil)
# Get the identity and user if they exist
identity = Identity.find_for_oauth(auth)
# If a signed_in_resource is provided it always overrides the existing user
# to prevent the identity being locked with accidentally created accounts.
# Note that this may leave zombie accounts (with no associated identity) which
# can be cleaned up at a later date.
user = signed_in_resource ? signed_in_resource : identity.user
# Create the user if needed
if user.nil?
# Get the existing user by email if the provider gives us a verified email.
# If no verified email was provided we assign a temporary email and ask the
# user to verify it on the next step via RegistrationsController.finish_signup
email_is_verified = auth.info.email && (auth.info.verified || auth.info.verified_email)
email = auth.info.email if email_is_verified
user = User.where(email: email).first if email
# Create the user if it's a new registration
if user.nil?
user = User.new(
username: auth.info.nickname || auth.extra.raw_info.name.parameterize('-') || auth.uid,
email: email ? email : "#{OMNIAUTH_EMAIL_PREFIX}-#{auth.uid}-#{auth.provider}.com",
password: Devise.friendly_token[0,20]
)
user.skip_confirmation!
user.save!
end
end
# Associate the identity with the user if needed
if identity.user != user
identity.user = user
identity.save!
end
user
end
def name
organization? ? organization.name : username
end
@@ -67,4 +114,8 @@ class User < ActiveRecord::Base
e.present? ? where(email: e) : none
end
def email_provided?
!!(email && email !~ OMNIAUTH_EMAIL_REGEX) ||
!!(unconfirmed_email && unconfirmed_email !~ OMNIAUTH_EMAIL_REGEX)
end
end

View File

@@ -0,0 +1,5 @@
<br/>
<%= link_to t("omniauth.twitter.sign_in"), user_omniauth_authorize_path(:twitter), class: 'button radius expand' %>
<hr/>

View File

@@ -7,11 +7,11 @@
<%= link_to(t("devise_views.menu.login_items.logout"), destroy_user_session_path, method: :delete) %>
</li>
<% else %>
<li>
<%= link_to(t("devise_views.menu.login_items.login"), new_user_session_path) %>
</li>
<li>
<%= link_to(t("devise_views.menu.login_items.signup"), new_user_registration_path, class: "button radius small") %>
</li>
<li>
<%= link_to(t("devise_views.menu.login_items.login"), new_user_session_path) %>
</li>
<li>
<%= link_to(t("devise_views.menu.login_items.signup"), new_user_registration_path, class: "button radius small") %>
</li>
<% end %>
</ul>

View File

@@ -3,6 +3,8 @@
<div class="panel">
<h2><%= t("devise_views.sessions.new.title") %></h2>
<%= render 'devise/omniauth_form' %>
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
<div class="row">
<div class="small-12 columns">

View File

@@ -0,0 +1,13 @@
<div class="auth row">
<div class="small-12 medium-8 large-5 column small-centered">
<div class="panel">
<h1><%= t('omniauth.finish_signup.title') %></h1>
<%= form_for current_user, as: :user, url: do_finish_signup_path, html: { role: 'form'} do |f| %>
<%= render 'shared/errors', resource: current_user %>
<%= f.email_field :email, placeholder: t("devise_views.users.registrations.new.email_label"), value: nil %>
<%= f.submit t("devise_views.users.registrations.new.submit"), class: 'button radius' %>
<% end %>
</div>
</div>
</div>

View File

@@ -2,6 +2,9 @@
<div class="small-12 medium-8 large-5 column small-centered">
<div class="panel">
<h2><%= t("devise_views.users.registrations.new.title") %></h2>
<%= render 'devise/omniauth_form' %>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= render 'shared/errors', resource: resource %>

View File

@@ -239,6 +239,7 @@ Devise.setup do |config|
# Add a new OmniAuth provider. Check the wiki for more information on setting
# up on your models and hooks.
# config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo'
config.omniauth :twitter, Rails.application.secrets.twitter_key, Rails.application.secrets.twitter_secret
# ==> Warden configuration
# If you want to use other strategies, that are not supported by Devise, or

View File

@@ -1,4 +0,0 @@
Rails.application.config.middleware.use OmniAuth::Builder do
provider :developer unless Rails.env.production?
provider :twitter, Rails.application.secrets.twitter_key, Rails.application.secrets.twitter_secret
end

View File

@@ -148,3 +148,9 @@ en:
all: "You are not authorized to %{action} %{subject}."
welcome:
last_debates: Last debates
omniauth:
finish_signup:
title: Add Email
twitter:
sign_in: Sign in with Twitter
or: or

View File

@@ -148,3 +148,9 @@ es:
all: "No tienes permiso para realizar la acción '%{action}' sobre %{subject}."
welcome:
last_debates: Últimos debates
omniauth:
finish_signup:
title: Añade tu email
twitter:
sign_in: Inicia sessión con Twitter
or: o

View File

@@ -1,10 +1,19 @@
Rails.application.routes.draw do
devise_for :users, controllers: { registrations: 'users/registrations' }
devise_for :users, controllers: {
registrations: 'users/registrations',
omniauth_callbacks: 'users/omniauth_callbacks'
}
devise_for :organizations, class_name: 'User',
controllers: {
registrations: 'organizations/registrations',
sessions: 'devise/sessions'
}
sessions: 'devise/sessions',
},
skip: [:omniauth_callbacks]
devise_scope :user do
get :finish_signup, to: 'users/registrations#finish_signup'
patch :do_finish_signup, to: 'users/registrations#do_finish_signup'
end
# The priority is based upon order of creation: first created -> highest priority.
# See how all your routes lay out with "rake routes".