Search code examples
ruby-on-railsrubyauthenticationdeviseruby-on-rails-6

Rails 6, allow user to go to expected page after authentication


When a User signs in Devise taken to a new_car_path per the after_sign_in_path_for() method. However, there are situations where the User will try to go to a specific page that needs to be authenticated.

My example, logged out user goes directly to "domain.com/compliance-form", this page requires user to be authenticated before reaching it, so as expected, it redirects to a sign in form. When the user signs in, it takes them to the "after_sign_in_path_for" path, which in this care is the "new_car_path".

However, since the user know exactly the page they want to go to, I want them to go to "compliance_form_path" "domain.com/compliance-form".

How do I accomplish this?

  def after_sign_in_path_for(_resource=nil)
    new_car_path
  end

If I add stored_location_for(resource) as user11350468 recommended:

def after_sign_in_path_for(resource = nil)
  stored_location_for(resource) || new_car_path
end

I get the following error,

NoMethodError in SessionsController#create undefined method `user_url' for #SessionsController:0x00007fc9f9bd1148 Did you mean? search_url

THen added:

class ApplicationController < ActionController::Base
  before_action :store_user_location!, if: :storable_location?
  
  def storable_location?
    request.get? && is_navigational_format? && !devise_controller? && !request.xhr? 
  end

  def store_user_location!
    store_location_for(:user, request.fullpath)
  end
end

Even after adding before action in controller, I get the same error, here are the logs..

Started GET "/compliance_form for 127.0.0.1 at 2020-12-17 11:26:14 -0600
Processing by ComplianceController#new as HTML
Completed 401 Unauthorized in 4ms (ActiveRecord: 0.0ms | Allocations: 411)

Started GET "/users/sign_in" for 127.0.0.1 at 2020-12-17 11:26:14 -0600
Processing by SessionsController#new as HTML
  Rendering devise/sessions/new.html.erb within layouts/devise
  Rendered devise/sessions/new.html.erb within layouts/devise (Duration: 20.3ms | Allocations: 9174)
  Rendered layouts/_head.html.erb (Duration: 361.5ms | Allocations: 235189)
  Rendered layouts/_devise_navbar.html.erb (Duration: 3.7ms | Allocations: 901)
  Rendered layouts/_footer.html.erb (Duration: 0.1ms | Allocations: 5)
Completed 200 OK in 399ms (Views: 395.8ms | ActiveRecord: 0.0ms | Allocations: 249472)

Started POST "/users/sign_in" for 127.0.0.1 at 2020-12-17 11:26:21 -0600
Processing by SessionsController#create as HTML
  Parameters: {"authenticity_token"=>"IDqVSy1swedds22AEqMEzQQnamz7I+gysdfbNlPPhsRTMJGZkWCgWFYq1vg/3Af2Af5xjchnzdfgoH6m7wHXEA==", "user"=>{"email"=>"name@domain.com", "password"=>"[FILTERED]", "remember_me"=>"0"}, "commit"=>"Continue"}
  User Load (1.8ms)  SELECT "users".* FROM "users" WHERE "users"."email" = $1 LIMIT $2  [["email", "name@domain.com"], ["LIMIT", 1]]
  ↳ app/controllers/sessions_controller.rb:6:in `create'
  User Load (0.9ms)  SELECT "users".* FROM "users" WHERE "users"."email" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["email", "name@domain.com"], ["LIMIT", 1]]
  ↳ app/controllers/sessions_controller.rb:11:in `create'
   (0.6ms)  BEGIN
  ↳ app/controllers/sessions_controller.rb:11:in `create'
  User Update (1.0ms)  UPDATE "users" SET "current_sign_in_at" = $1, "last_sign_in_at" = $2, "sign_in_count" = $3, "updated_at" = $4 WHERE "users"."id" = $5  [["current_sign_in_at", "2020-12-17 17:26:21.278696"], ["last_sign_in_at", "2020-12-17 17:24:43.660819"], ["sign_in_count", 31], ["updated_at", "2020-12-17 17:26:21.279770"], ["id", 2]]
  ↳ app/controllers/sessions_controller.rb:11:in `create'
   (4.6ms)  COMMIT
  ↳ app/controllers/sessions_controller.rb:11:in `create'
Redirected to 
Completed 500 Internal Server Error in 610ms (ActiveRecord: 9.9ms | Allocations: 188187)
NoMethodError (undefined method `user_url' for #<SessionsController:0x00007fae28d6a438>
Did you mean?  search_url):
  
app/controllers/sessions_controller.rb:11:in `create'

UPDATE:

My syntax was slightly off, I should have posted exact code (lesson learned):

  def after_sign_in_path_for(_resource=nil)
    return stored_location_for(resource) if stored_location_for(resource).present?
    return car_index_path if current_user.car?
    new_car_path
  end

What is happening is when stored_location_for(resource) gets called, it becomes nil, and so the If statement returns true, but the stored_location_for(resource) value returns nil.

Hence causing the error. Stored_location_for() solution below works great!


Solution

  • Try the below with helper stored_location_for

    Returns and delete (if it's navigational format) the url stored in the session for the given scope. Useful for giving redirect backs after sign up:

    According to the the devise wiki, please add the below before_action in your application_controller:

    class ApplicationController < ActionController::Base
      before_action :store_user_location!, if: :storable_location?
      
      def storable_location?
        request.get? && is_navigational_format? && !devise_controller? && !request.xhr? 
      end
    
      def store_user_location!
        store_location_for(:user, request.fullpath)
      end
    end
    

    And then in after_sign_in_path_for:

    def after_sign_in_path_for(resource = nil)
      stored_location_for(resource) || new_car_path
    end