title: Rails 4 - Devise Omniauth (multiple strategies)
body: <p>I'm trying to make an app in Rails 4.  I have been trying for the last 3 years (except for 10 days), to get devise to work.</p>
  
  <p>I'm trying to follow this tutorial: <a href="http://sourcey.com/rails-4-omniauth-using-devise-with-twitter-facebook-and-linkedin/">http://sourcey.com/rails-4-omniauth-using-devise-with-twitter-facebook-and-linkedin/</a></p>
  
  <p>Please don't recommend other tutorials / gem documentation. I have tried at least 30 other tutorials and the gem documentation is full of errors and components that I don't understand.</p>
  
  <p>My current problem is that when I get to the finish sign up step in this tutorial, the form asks me for my email address.</p>
  
  <p>The users controller has a finish signup method as:</p>
  
  <pre><code>def finish_signup
      # authorize! :update, @user 
      if request.patch? &amp;&amp; params[:user] #&amp;&amp; params[:user][:email]
        if @user.update(user_params)
          @user.skip_reconfirmation!
          # sign_in(@user, :bypass =&gt; true)
          # redirect_to root_path, notice: 'Your profile was successfully updated.'
          # redirect_to [@user, @user.profile || @user.build_profile]
          sign_in_and_redirect(@user, :bypass =&gt; true)
        else
          @show_errors = true
        end
      end
    end
  </code></pre>
  
  <p>When I try this, I get this error:</p>
  
  <pre><code>undefined method `match' for {:host=&gt;"localhost", :port=&gt;3000}:Hash
  </code></pre>
  
  <p>The error points at this line:</p>
  
  <pre><code>        &lt;div class="intpol3"&gt;&lt;%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %&gt;&lt;/div&gt;
  </code></pre>
  
  <p>My development environment is set up to include all the config details for my email sender.</p>
  
  <p>When I try the same step in production mode, I get this error:</p>
  
  <pre><code>ActionView::Template::Error (No route matches {:action=&gt;"show", :controller=&gt;"profiles", :id=&gt;nil} missing required keys: [:id]):
  </code></pre>
  
  <p>It's looking for a profile id because I have an after_create action in my user model as:</p>
  
  <pre><code>after_create :gen_profile
  
    def gen_profile
      Profile.create(user: self) # Associations must be defined correctly for this syntax, avoids using ID's directly.
      # Profile.save
    end
  </code></pre>
  
  <p>My other issue with this tutorial is that the fields in the identity table aren't being populated.</p>
  
  <p>I'd love to find someone that has successfully implemented this tutorial or can see how to make this work.</p>
  
  <p>My code is:</p>
  
  <p>gemfile</p>
  
  <pre><code>gem 'devise', '3.4.1'
  gem 'devise_zxcvbn'
  gem 'omniauth'
  gem 'omniauth-oauth2', '1.3.1'
  gem 'omniauth-google-oauth2'
  gem 'omniauth-facebook'
  gem 'omniauth-twitter'
  gem 'omniauth-linkedin-oauth2'
  gem 'google-api-client', require: 'google/api_client'
  </code></pre>
  
  <p>routes</p>
  
  <pre><code>devise_for :users, #class_name: 'FormUser',
               :controllers =&gt; {
                  :registrations =&gt; "users/registrations",
                  # :omniauth_callbacks =&gt; "users/authentications"
                  :omniauth_callbacks =&gt; 'users/omniauth_callbacks'
             }
  
    # get '/auth/:provider/callback' =&gt; 'users/authentications#create'
    # get '/authentications/sign_out', :to =&gt; 'users/authentications#destroy' 
  
    # PER SOURCEY TUTORIAL ----------
    match '/users/:id/finish_signup' =&gt; 'users#finish_signup', via: [:get, :patch], :as =&gt; :finish_signup
  
  resources :users do
       resources :profiles, only: [:new, :create]
    end
  </code></pre>
  
  <p>user.rb</p>
  
  <p>class User &lt; ActiveRecord::Base</p>
  
  <pre><code>  TEMP_EMAIL_PREFIX = 'change@me'
    TEMP_EMAIL_REGEX = /\Achange@me/
  
    devise :database_authenticatable, :registerable,
           :recoverable, :rememberable, :trackable,
            :confirmable, :lockable,
           # :zxcvbnable,
           :omniauthable, :omniauth_providers =&gt; [:facebook, :linkedin, :twitter, :google_oauth2 ]
  
  
  
  
  
    # --------------- associations
  
    has_many :authentications, :dependent =&gt; :delete_all
  
    has_one :profile
  
    has_many :identities
  
  
    # --------------- scopes
  
    # --------------- validations
  
     # validates_presence_of :first_name, :last_name
     validates_uniqueness_of :email
  
    # per sourcey tutorial - how do i confirm email registrations are unique?
    # this is generating an error about the options in the without function -- cant figure out the solution
    validates_format_of :email, :without =&gt; TEMP_EMAIL_REGEX, on: :update 
  
  
    # --------------- class methods
  
  
  # sourcey tutorial
  
   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
  
      # p '11111'
  
      # Create the user if needed
      if user.nil?
        # p 22222
        # 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 &amp;&amp; (auth.info.verified || auth.info.verified_email)
        email = auth.info.email if email_is_verified # take out this if stmt for chin yi's solution
        user = User.where(:email =&gt; email).first if email
  
        # Create the user if it's a new registration
        if user.nil?
          # p 33333
          user = User.new(
            # at least one problem with this is that each provider uses different terms to desribe first name/last name/email. See notes on linkedin above
            first_name: auth.info.first_name,
            last_name: auth.info.last_name,
            email: email ? email : "#{TEMP_EMAIL_PREFIX}-#{auth.uid}-#{auth.provider}.com",
            #username: auth.info.nickname || auth.uid,
            password: Devise.friendly_token[0,20])
  # fallback for name fields - add nickname to user table
          # debugger
  
          # if email_is_verified
             user.skip_confirmation!
          # end
          # 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 &amp;&amp; TEMP_EMAIL_REGEX !~ self.email
    end
  </code></pre>
  
  <p>users controller</p>
  
  <pre><code>class UsersController &lt; ApplicationController
  
  before_action :set_user, only: [:index, :show, :edit, :update, :finish_signup, :destroy]
  
  # i added finish_signup to the set_user action (not shown in tutorial)
  
    def index
      # if params[:approved] == "false"
      #   @users = User.find_all_by_approved(false)
      # else
        @users = User.all
      # end
  
    end
  
    # GET /users/:id.:format
    def show
      # authorize! :read, @user
    end
  
    # GET /users/:id/edit
    def edit
      # authorize! :update, @user
    end
  
    # PATCH/PUT /users/:id.:format
    def update
      # authorize! :update, @user
      respond_to do |format|
        if @user.update(user_params)
          sign_in(@user == current_user ? @user : current_user, :bypass =&gt; true)
          format.html { redirect_to @user, notice: 'Your profile was successfully updated.' }
          format.json { head :no_content }
        else
          format.html { render action: 'edit' }
          format.json { render json: @user.errors, status: :unprocessable_entity }
        end
      end
    end
  
    # GET/PATCH /users/:id/finish_signup
    def finish_signup
      # authorize! :update, @user 
      if request.patch? &amp;&amp; params[:user] #&amp;&amp; params[:user][:email]
        if @user.update(user_params)
          @user.skip_reconfirmation!
          # sign_in(@user, :bypass =&gt; true)
          # redirect_to root_path, notice: 'Your profile was successfully updated.'
          # redirect_to [@user, @user.profile || @user.build_profile]
          sign_in_and_redirect(@user, :bypass =&gt; true)
        else
          @show_errors = true
        end
      end
    end
  
    # DELETE /users/:id.:format
    def destroy
      # authorize! :delete, @user
      @user.destroy
      respond_to do |format|
        format.html { redirect_to root_url }
        format.json { head :no_content }
      end
    end
  
    private
      def set_user
        @user = User.find(params[:id])
      end
  
      def user_params
        # params.require(:user).permit(policy(@user).permitted_attributes)
        accessible = [ :first_name, :last_name, :email, :avatar ] # extend with your own params
        accessible &lt;&lt; [ :password, :password_confirmation ] unless params[:user][:password].blank?
        # accessible &lt;&lt; [:approved] if user.admin
        params.require(:user).permit(accessible)
      end
  
  end
  </code></pre>
  
  <p>omniauth callbacks controller</p>
  
  <pre><code>class Users::OmniauthCallbacksController &lt; 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
  
    # , current_user has been deleted from the end of line 51
    #come back to put current_user into fidn by oauth so i can link other accounts - i have added this back for the purpose of solving the current problem
  
            # puts current_user.inspect
             # sign_in_and_redirect [@user, @user.profile || @user.build_profile]
  
            # sign_in_and_redirect_user(:user, event: :authentication)
  
  
    [:twitter, :facebook, :linkedin, :google_oauth2].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
  </code></pre>
  
  <p>registrations controller</p>
  
  <pre><code>class Users::RegistrationsController &lt; Devise::RegistrationsController 
  
  
  
  
        protected
  
        def after_sign_up_path_for(resource)
        profile_path(resource)
    end
  
  
    private
      def user_params
            params.require(:user).permit(:first_name, :last_name, :email, :password )
      end
  
      end
  </code></pre>
  
  <p>Identity.rb</p>
  
  <pre><code>class Identity &lt; ActiveRecord::Base
  
  
    belongs_to :user
    validates_presence_of :uid, :provider
    validates_uniqueness_of :uid, :scope =&gt; :provider
  
  
  
    def self.find_for_oauth(auth)
      find_or_create_by(uid: auth.uid, provider: auth.provider)
    end
  
  
  end
  </code></pre>
  
  <p>Identities controller</p>
  
  <pre><code>class IdentitiesController &lt; ApplicationController
    before_action :set_identity, only: [:show, :edit, :update, :destroy]
    before_action :authenticate_user!
  
    # GET /identities
    # GET /identities.json
    def index
      @identities = Identity.all
    end
  
    # GET /identities/1
    # GET /identities/1.json
    def show
    end
  
    # GET /identities/new
    def new
      @identity = Identity.new
    end
  
    # GET /identities/1/edit
    def edit
    end
  
    # POST /identities
    # POST /identities.json
    def create
      @identity = Identity.new(identity_params)
  
      respond_to do |format|
        if @identity.save
          format.html { redirect_to @identity, notice: 'Identity was successfully created.' }
          format.json { render :show, status: :created, location: @identity }
        else
          format.html { render :new }
          format.json { render json: @identity.errors, status: :unprocessable_entity }
        end
      end
    end
  
    # PATCH/PUT /identities/1
    # PATCH/PUT /identities/1.json
    def update
      respond_to do |format|
        if @identity.update(identity_params)
          format.html { redirect_to @identity, notice: 'Identity was successfully updated.' }
          format.json { render :show, status: :ok, location: @identity }
        else
          format.html { render :edit }
          format.json { render json: @identity.errors, status: :unprocessable_entity }
        end
      end
    end
  
    # DELETE /identities/1
    # DELETE /identities/1.json
    def destroy
      @identity.destroy
      respond_to do |format|
        format.html { redirect_to identities_url, notice: 'Identity was successfully destroyed.' }
        format.json { head :no_content }
      end
    end
  
    private
      # Use callbacks to share common setup or constraints between actions.
      def set_identity
        @identity = Identity.find(params[:id])
      end
  
      # Never trust parameters from the scary internet, only allow the white list through.
      def identity_params
        params[:identity]
      end
  end
  </code></pre>
  
  <p>devise mailer - confirmation</p>
  
  <pre><code>        &lt;div class="intpol3"&gt;&lt;%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %&gt;&lt;/div&gt;
  </code></pre>
  
  <p>SUMMARY OF CURRENT PROBLEMS:</p>
  
  <ol>
  <li><p>In development mode: There is a problem with the link to the confirmation token. I can't find any materials that indicate why this might arise. The error is:(ActionView::Template::Error (undefined method `match' for {:host=>"localhost", :port=>3000}:Hash):</p></li>
  <li><p>In production mode, there is an error with user looking for profile id. The error message is: ActionView::Template::Error (No route matches {:action=>"show", :controller=>"profiles", :id=>nil} missing required keys: [:id]):</p></li>
  </ol>
  
  <p>My profiles routes are:</p>
  
  <pre><code>resources :profiles, only: [:show, :edit, :update, :destroy]
  resources :users do
       resources :profiles, only: [:new, :create]
    end
  </code></pre>
  
  <ol start="3">
  <li>None of the fields in the identity model are populating. They are all showing as nil.</li>
  </ol>
  
  <p>THINGS DONE DIFFERENTLY THAN AS SHOWN IN THE TUTORIAL:</p>
  
  <ol>
  <li><p>I also allow email sign up</p></li>
  <li><p>I add 'finish_sign_up' to the set_user before action in the users controller</p></li>
  <li><p>I add g+ strategy (which means my gems are slightly different)</p></li>
  <li><p>My new user method does not use raw info. It uses oauth processed info.</p></li>
  <li><p>My redirect in the finish sign up method is slightly different, although I've commented that out and gone back to the way it is set out in the tutorial to try to get this working (although the above problems are repeating).</p></li>
  </ol>
  
  <p>I'm going crazy trying to solve these problems. I'd say 3 years is way too long to be stuck on this problem. If anyone can help, I'd pay it forward 10x and then some. Thank you.</p>