ruby on rails

I am using Devise + OAuth on Ruby on Rails.

I have modified the after_sign_in_path in the ApplicationController as such:

def after_sign_in_path_for(resource)
  if session[:longitude]
    puts user_signed_in?
    new_questionnaire_path
  else
    super
  end
end

When it is called, the log outputs:

true
Redirected to http://localhost:3000/questionnaires/new

I have a before_action on the new questionnaire route as follows:

  def require_login
      unless current_user
        redirect_to new_user_registration_path, notice: 'Please sign in to get started!'
      end
  end

The first time a user signs in via OAuth, the current_user somehow becomes nil and the log shows the following:

Started GET "/questionnaires/new" for 127.0.0.1 at 2018-01-06 17:30:50 -0800
Processing by QuestionnairesController#new as HTML
User Load (0.4ms)  SELECT  "users".* FROM "users" WHERE "users"."id" IS NULL 
ORDER BY "users"."id" ASC LIMIT $1  [["LIMIT", 1]]
Redirected to http://localhost:3000/users/sign_up
Filter chain halted as :require_login rendered or redirected

If the user signs in a second time, they are redirected to the new_questionnaire_path and do not hit the before_action a second time.

Why might this be happening?

EDIT: Here is the contents of my OmniAuthCallbacksController. I don't think this is relevant since the after_sign_in_path_for(resource) gets called after the sign_in_with(provider_name) but maybe I am missing something?

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
skip_before_action :verify_authenticity_token

  def sign_in_with(provider_name)
    @user = User.from_omniauth(request.env["omniauth.auth"])
    @user.profile
    sign_in_and_redirect @user, :event => :authentication
    set_flash_message(:notice, :success, :kind => provider_name) if 
    is_navigational_format?
  end

  def facebook
    sign_in_with "Facebook"
  end

  def linkedin
    sign_in_with "LinkedIn"
    @user.linked_in_data = request.env["omniauth.auth"]
    @user.save
    @profile = Profile.find_by_user_id(@user.id)
    @profile.first_name = @user.linked_in_data['info']['first_name']
    @profile.last_name = @user.linked_in_data['info']['last_name']
    @profile.title = @user.linked_in_data['info']['description']
    @profile.industry = @user.linked_in_data['extra']['raw_info']['industry']
    @profile.save
  end

  def twitter
    sign_in_with "Twitter"
  end

  def google_oauth2
    sign_in_with "Google"
  end

  def developer
    sign_in_with "Developer"
  end

end

you need to follow the instruction for the oauth devise implementation.

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def facebook
    # You need to implement the method below in your model (e.g. app/models/user.rb)
    @user = User.from_omniauth(request.env["omniauth.auth"])

    if @user.persisted?
      sign_in_and_redirect @user, event: :authentication #this will throw if @user is not activated
      set_flash_message(:notice, :success, kind: "Facebook") if is_navigational_format?
    else
      session["devise.facebook_data"] = request.env["omniauth.auth"]
      redirect_to new_user_registration_url
    end
  end

  def failure
    redirect_to root_path
  end
end

I want to ask you where did you find this code. The main problem is that I don't see a logic for error handling after User.from_omniauth , so maybe something is failing there and not triggering error.

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
skip_before_action :verify_authenticity_token

  def sign_in_with(provider_name)
    @user = User.from_omniauth(request.env["omniauth.auth"])
    @user.profile
    sign_in_and_redirect @user, :event => :authentication
    set_flash_message(:notice, :success, :kind => provider_name) if 
    is_navigational_format?
  end

I can not dig deep in your problem (the redirection after sign in), because I feel that your logic is wrong. For example if you fall def linkedin to log in , I will always run @profile.first_name and @profile.save even if the user is not signing up, but logging in with @user = User.from_omniauth(request.env["omniauth.auth"])

 def linkedin
    sign_in_with "LinkedIn"
    @user.linked_in_data = request.env["omniauth.auth"]
    @user.save
    @profile = Profile.find_by_user_id(@user.id)
    @profile.first_name = @user.linked_in_data['info']['first_name']
    @profile.last_name = @user.linked_in_data['info']['last_name']
    @profile.title = @user.linked_in_data['info']['description']
    @profile.industry = @user.linked_in_data['extra']['raw_info']['industry']
    @profile.save
  end

This is the self.from_omniauth(auth) method

def self.from_omniauth(auth)
  where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
    user.email = auth.info.email
    user.password = Devise.friendly_token[0,20]
    user.name = auth.info.name   # assuming the user model has a name
    user.image = auth.info.image # assuming the user model has an image
    # If you are using confirmable and the provider(s) you use validate emails, 
    # uncomment the line below to skip the confirmation emails.
    # user.skip_confirmation!
  end
end

I was able to resolve this by adding error handling per Fabrizio's suggestion, which showed that @user.persisted? was returning false.

puts @user.errors.to_a told me that "Password cannot be blank", which I did not realize was the case, so I added "user.password = Devise.friendly_token[0,20]" to the self.from_omniauth method in my User model, which solved the issue.

What I still don't entirely understand is how it is possible that a User was being added to the database while at the same time @user.persisted? was returning false.

Shown below is my updated controller with the error handling, the puts statement I used to find the source of the issue, and code to only execute the Profile updates on the first sign in per Fabrizio's excellent suggestion.

A reader with a particularly keen eye may also notice that the "@user.profile" line in the sign_in_with method has been removed. This method is used to build a profile for a User when they initially sign up, but I realized it would be cleaner to do this as an after_create method on the User model, so I've done that as well.

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  skip_before_action :verify_authenticity_token

  def sign_in_with(provider_name)
    @user = User.from_omniauth(request.env["omniauth.auth"])

    puts @user.errors.to_a

    if @user.persisted?
      sign_in_and_redirect @user, :event => :authentication
      set_flash_message(:notice, :success, :kind => provider_name) if is_navigational_format?
    else
      session["devise.#{provider_name}_data"] = request.env["omniauth.auth"]
      redirect_to new_user_registration_url
    end
  end

  def facebook
    sign_in_with "Facebook"
  end

  def linkedin
    sign_in_with "LinkedIn"
    @user.linked_in_data = request.env["omniauth.auth"]
    @user.save

    if @user.sign_in_count == 1
      @profile = Profile.find_by_user_id(@user.id)
      @profile.first_name = @user.linked_in_data['info']['first_name']
      @profile.last_name = @user.linked_in_data['info']['last_name']
      @profile.title = @user.linked_in_data['info']['description']
      @profile.industry = @user.linked_in_data['extra']['raw_info']['industry']
      @profile.save
    end

  end

  def twitter
    sign_in_with "Twitter"
  end

  def google_oauth2
    sign_in_with "Google"
  end

  def developer
    sign_in_with "Developer"
  end

  def failure
    redirect_to root_path
  end

end
链接地址: http://www.djcxy.com/p/47452.html

上一篇: Youtube嵌入式播放列表插入播放按钮而不是第一个视频

下一篇: 红宝石在轨道上