Reputation: 1955
I have an app that I'm trying to add authentication using twitter, linkedin and facebook as well as my custom authentication using devise
My Gemfile is as follows
gem 'devise'
gem 'omniauth'
gem 'omniauth-facebook'
gem 'omniauth-linkedin'
gem 'omniauth-twitter'
gem 'oauth2'
gem 'figaro'
I am using devise 3.2.4
I have a user.rb model which is as follows
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable, :omniauth_providers => [:facebook, :twitter, :linkedin]
TEMP_EMAIL_PREFIX = 'change@me'
TEMP_EMAIL_REGEX = /\Achange@me/
validates_format_of :email, :without => TEMP_EMAIL_REGEX, on: :update
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 UsersController.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.extra.raw_info.name,
#username: auth.info.nickname || auth.uid,
email: email ? email : "#{TEMP_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 email_verified?
self.email && self.email !~ TEMP_EMAIL_REGEX
end
end
My identity.rb is as follows
class Identity < ActiveRecord::Base
belongs_to :user
validates_presence_of :uid, :provider
validates_uniqueness_of :uid, :scope => :provider
def self.find_for_oauth(auth)
identity = find_by(provider: auth.provider, uid: auth.uid)
identity = create(uid: auth.uid, provider: auth.provider) if identity.nil?
identity
end
end
users_controller.rb
class UsersController < ApplicationController
before_action :set_user, :finish_signup
before_filter :ensure_signup_complete, only: [:new, :create, :update, :destroy]
def finish_signup
if request.patch? && params[:user] #&& params[:user][:email]
if current_user.update(user_params)
current_user.skip_reconfirmation!
sign_in(current_user, :bypass => true)
redirect_to current_user, notice: 'Your profile was successfully updated.'
else
@show_errors = true
end
end
end
private
def set_user
@user = User.find(params[:id])
end
def user_params
accessible = [ :username, :email ] # extend with your own params
accessible << [ :password, :password_confirmation ] unless params[:user][:password].blank?
params.require(:user).permit(accessible)
end
end
omniauth_callbacks_controller.rb
class 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, :linked_in].each do |provider|
provides_callback_for provider
end
def after_sign_in_path_for(resource)
if resource.email_verified?
super resource
else
finish_signup_path(resource)
end
end
end
/config/routes.rb
devise_for :users, :controllers => { omniauth_callbacks: 'omniauth_callbacks' }
match '/profile/:id/finish_signup' => 'users#finish_signup', via: [:get, :patch], :as => :finish_signup
get 'home/index'
root to: "home#index"
config/initializers/devise.rb
Devise.setup do |config|
config.omniauth :facebook, ENV["FACEBOOK_KEY"], ENV["FACEBOOK_SECRET"]
config.omniauth :twitter, ENV["TWITTER_KEY"], ENV["TWITTER_SECRET"]
config.omniauth :linked_in, ENV["LINKEDIN_KEY"], ENV["LINKEDIN_SECRET"]
...
end
application_controller.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
def ensure_signup_complete
# Ensure we don't go into an infinite loop
return if action_name == 'finish_signup'
# Redirect to the 'finish_signup' page if the user
# email hasn't been verified yet
if current_user && !current_user.email_verified?
redirect_to finish_signup_path(current_user)
end
end
end
I have tried changing my routes.rb to
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }
From my application's root directory
mkdir app/controllers/users mv app/controllers/omniauth_callbacks_controller.rb app/controllers/users
I then changed the omniauth_callbacks_controller.rb from
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
to
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
In my routes.rb, I changed
devise_for :users, :controllers => { omniauth_callbacks: 'omniauth_callbacks' }
devise_for :users, :controllers => { omniauth_callbacks: 'users/omniauth_callbacks' }
I then restarted my server and when I try logging in using linkedin I get the error,
The action 'linkedin' could not be found for Users::OmniauthCallbacksController
What have I missed?
The output of rake routes is
Prefix Verb URI Pattern Controller#Action
new_user_session GET /users/sign_in(.:format) devise/sessions#new
user_session POST /users/sign_in(.:format) devise/sessions#create
destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy
user_omniauth_authorize GET|POST /users/auth/:provider(.:format) users/omniauth_callbacks#passthru {:provider=>/facebook|twitter|linkedin/}
user_omniauth_callback GET|POST /users/auth/:action/callback(.:format) users/omniauth_callbacks#(?-mix:facebook|twitter|linkedin)
user_password POST /users/password(.:format) devise/passwords#create
new_user_password GET /users/password/new(.:format) devise/passwords#new
edit_user_password GET /users/password/edit(.:format) devise/passwords#edit
PATCH /users/password(.:format) devise/passwords#update
PUT /users/password(.:format) devise/passwords#update
cancel_user_registration GET /users/cancel(.:format) devise/registrations#cancel
user_registration POST /users(.:format) devise/registrations#create
new_user_registration GET /users/sign_up(.:format) devise/registrations#new
edit_user_registration GET /users/edit(.:format) devise/registrations#edit
PATCH /users(.:format) devise/registrations#update
PUT /users(.:format) devise/registrations#update
DELETE /users(.:format) devise/registrations#destroy
finish_signup GET|PATCH /profile/:id/finish_signup(.:format) users#finish_signup
home_index GET /home/index(.:format) home#index
root GET / home#index
Upvotes: 0
Views: 849
Reputation: 3574
You have to specify correct provider name. In your code, I can see that you are using the name linked_in
which is incorrect, you have to change all of them to linkedin
Regards
Upvotes: 4