Mutuma
Mutuma

Reputation: 1955

The action 'linkedin' could not be found for OmniauthCallbacksController

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

Answers (1)

The Lazy Log
The Lazy Log

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

Related Questions