Rails 6, allow user to go to expected page after authentication - ruby-on-rails

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!

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

Related

No automatic sign_in after modifying devise registration controller

I initially wanted to send my user to a different page after sign_up. This works now, as I created a registrations controller. The problem now is that the user is prompted to login right after sign_up. Of course I want him to be automatically signed_in after sign_up. Any idea how I can fix that? I found this explanation but both answers do not work for me: Rails: Devise login after sign up.
Here is my registrations controller:
class RegistrationsController < Devise::RegistrationsController
before_action :configure_permitted_parameters, if: :devise_controller?
def start_date
#user =current_user
end
protected
def configure_permitted_parameters
update_attrs = [:password, :password_confirmation, :current_password, :start_date]
devise_parameter_sanitizer.permit(:sign_up, keys: [:applications, :job_category, :job_status])
end
def after_sign_up_path_for(resource)
'/profiles/new' # Or :prefix_to_your_route
end
end
here are my application Logs after signup:
Started GET "/users/sign_up?job_category=IT+%26+Development&job_status=Other+Category&vacancy_id=general+application" for ::1 at 2019-08-08 15:32:17 +0200
Processing by RegistrationsController#new as HTML
Parameters: {"job_category"=>"IT & Development", "job_status"=>"Other Category", "vacancy_id"=>"general application"}
Rendering devise/registrations/new.html.erb within layouts/application
Rendered devise/registrations/new.html.erb within layouts/application (1.1ms)
Rendered shared/_navbar.html.erb (1.0ms)
Rendered shared/_flashes.html.erb (0.4ms)
Completed 200 OK in 174ms (Views: 171.7ms | ActiveRecord: 0.0ms)
Started POST "/users" for ::1 at 2019-08-08 15:32:32 +0200
Processing by RegistrationsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Rb06/S6dB019mw8I46x0tJytVG7HNeVV23ZbuX/9Ykb9amYwI3bCLlk8AqNjTEGAR0qTy6rlCNZE1U6w8skslA==", "user"=>{"email"=>"testtesttest#test.de", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "applications"=>"\#{general application}", "job_category"=>"IT & Development", "job_status"=>"Other Category", "terms"=>"1"}, "commit"=>"Sign up"}
Can't verify CSRF token authenticity.
Unpermitted parameter: :terms
(0.5ms) BEGIN
User Exists (0.5ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = $1 LIMIT $2 [["email", "testtesttest#test.de"], ["LIMIT", 1]]
SQL (6.9ms) INSERT INTO "users" ("email", "encrypted_password", "created_at", "updated_at", "applications", "job_category", "job_status") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id" [["email", "testtesttest#test.de"], ["encrypted_password", "$2a$11$MlDweUU00bKQsNyA81/Cuu/o9HQSYJHM8FCInvVSaYVspvcs36oMS"], ["created_at", "2019-08-08 13:32:32.764097"], ["updated_at", "2019-08-08 13:32:32.764097"], ["applications", "{{\"general application\"}}"], ["job_category", "IT & Development"], ["job_status", "Other Category"]]
UserMailer#welcome: processed outbound mail in 0.2ms
(8.4ms) COMMIT
Redirected to http://localhost:3000/profiles/new
Completed 302 Found in 316ms (ActiveRecord: 16.2ms)
Started GET "/profiles/new" for ::1 at 2019-08-08 15:32:32 +0200
Processing by ProfilesController#new as HTML
Completed 401 Unauthorized in 3ms (ActiveRecord: 0.0ms)
Started GET "/users/sign_in" for ::1 at 2019-08-08 15:32:32 +0200
Processing by Devise::SessionsController#new as HTML
Rendering devise/sessions/new.html.erb within layouts/application
Rendered devise/shared/_links.html.erb (1.3ms)
Rendered devise/sessions/new.html.erb within layouts/application (11.0ms)
Rendered shared/_navbar.html.erb (1.4ms)
Rendered shared/_flashes.html.erb (0.5ms)
Completed 200 OK in 198ms (Views: 196.5ms | ActiveRecord: 0.0ms)
Change permitted params from:
def configure_permitted_parameters
update_attrs = [:password, :password_confirmation, :current_password, :start_date]
devise_parameter_sanitizer.permit(:sign_up, keys: [:applications, :job_category, :job_status])
end
to:
def configure_permitted_parameters
update_attrs = [:password, :password_confirmation, :current_password, :start_date]
devise_parameter_sanitizer.permit(:sign_up, keys: [ update_attrs, :applications, :job_category, :job_status])
end
And I think in profiles controller has a problem with before_action :authenticate_user! .
In terminal redirect has work.
You have a problem with terms, in your terminal:
Can't verify CSRF token authenticity.
Unpermitted parameter: :terms
Try changing the protect_from_forgery with: :exception to protect_from_forgery with: :exception, prepend: true
Ref: https://github.com/plataformatec/devise#controller-filters-and-helpers
Devise by default signout the session if the request is unverified.
Ref: from devise code(Devise::Controllers::Helpers),
# Overwrite Rails' handle unverified request to sign out all scopes,
# clear run strategies and remove cached variables.
def handle_unverified_request
super # call the default behaviour which resets/nullifies/raises
request.env["devise.skip_storage"] = true
sign_out_all_scopes(false)
end
Try this out if the first one solves the issue, https://github.com/plataformatec/devise/issues/2734 for solving the issue,
Change this:
devise_parameter_sanitizer.permit(:sign_up, keys: [:applications, :job_category, :job_status]
To this:
devise_parameter_sanitizer.permit(:sign_up, keys: [:applications, :job_category, :job_status, :terms]

Rails: Item not saving to database (routing with nested resources)

I'm running into a problem when trying to create a new object using nested resources in Rails. My routing is set up as:
resources :coins do
resources :questions
resources :events
end
When I attempt to create a new event, it does not save. Adding ! to event.save gave me an error that says "Validation failed: Coin must exist".
I don't have a validation set up for the coin (in the Event model at least, if that is what its referring to). Checking the log file shows the following, which as far as I can tell shows that the correct coin is selected:
Started GET "/coins/1/events/new" for 127.0.0.1 at 2018-01-24 18:52:19 -0500
Processing by EventsController#new as HTML
Parameters: {"coin_id"=>"1"}
[1m[36mCoin Load (0.1ms)[0m [1m[34mSELECT "coins".* FROM "coins" WHERE "coins"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
[1m[36mUser Load (0.2ms)[0m [1m[34mSELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
Rendering events/new.html.erb within layouts/application
Rendered events/new.html.erb within layouts/application (3.9ms)
Rendered layouts/_rails_defaults.html.erb (34.8ms)
Rendered layouts/_shim.html.erb (0.6ms)
Rendered layouts/_header.html.erb (6.2ms)
Completed 200 OK in 67ms (Views: 62.3ms | ActiveRecord: 0.3ms)
Started POST "/coins/1/events" for 127.0.0.1 at 2018-01-24 18:54:46 -0500
Processing by EventsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"dAXQ3uwxR2IN2rbAYD//gulHebIOdZWFPYCKnxcUKTuV4QnUp+SZYHMpZUsGOgEXQjOAnhFUO9MpJkIIAKcQlQ==", "event"=>{"content"=>"LKNA", "link"=>"asd"}, "commit"=>"Submit", "coin_id"=>"1"}
[1m[36mCoin Load (0.2ms)[0m [1m[34mSELECT "coins".* FROM "coins" WHERE "coins"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
[1m[36mUser Load (0.2ms)[0m [1m[34mSELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
[1m[35m (0.1ms)[0m [1m[36mbegin transaction[0m
[1m[35m (0.0ms)[0m [1m[31mrollback transaction[0m
Completed 401 Unauthorized in 66ms (ActiveRecord: 3.0ms)
ActiveRecord::RecordInvalid (Validation failed: Coin must exist):
app/controllers/events_controller.rb:23:in `create'
I have it set up the same way for my Questions model, as they are almost identical aside from names, and that one works with no issues. I'm at a loss as to what I'm doing incorrectly here.
controller:
class EventsController < ApplicationController
before_action :find_event, only: [:show, :edit, :update, :destroy ]
before_action :find_coin
def index
#events = Event.where(coin_id: #coin.id).order("created_at DESC")
end
def show
end
def new
#event = current_user.events.build
end
def create
#event = current_user.events.build(event_params)
if #event.save!
flash[:success] = "Event Saved"
redirect_to coin_event_path(#coin.id, #event.id)
else
flash[:notice] = "Event *NOT* Saved!!!"
render 'new'
#event.errors.full_messages
end
end
.
.
.
private
def find_event
#event = Event.find(params[:id])
end
def find_coin
#coin = Coin.find(params[:coin_id])
end
def event_params
params.require(:event).permit(:content, :link)
end
end
model:
class Event < ApplicationRecord
belongs_to :user
belongs_to :coin
end
coin model:
class Coin < ApplicationRecord
validates :link_name, :currency_name, presence: true
has_many :questions
has_many :events
end
If anyone has any idea what I may be doing wrong or any tips on how to figure it out, I would really appreciate the assistance.
Making an answer for anyone else who needs it and doesn't read comments :)
When you do:
#event = current_user.events.build(event_params)
You actually create an event related for the user, and it is not related to the coin anymore.
Try to set the coin as well for the event before saving:
#event.coin = #coin
Or you can do it the other way - build the event on the current coin, and then set the user:
#event = #coin.events.build(event_params)
#event.user = current_user
That way - the coin_id will be set automatically because you build an event on that coin.
I have solved the same issue adding 'optional: true' to belongs_to method.
belongs_to :user, optional: true
By default 'belongs_to' require the user_id.
You can use too: 'required: false'.
belongs_to :user, required: false
Good luck.

Devise Admin Edit User failing for password "can't be blank". What are the possible levels of code down to DB that can block this?

I've got Devise and omniauth gems. I more or less followed railscasts for multiple authentications so have Users and Authentications models.
I've managed to have a user update their own profile without a password when they have authentications.
class RegistrationsController < Devise::RegistrationsController
def update_resource(resource, params)
if current_user.authentications.empty?
resource.update_with_password(account_update_params)
else
params.except("current_password")
resource.update_without_password(account_update_params)
end
end
end
routes.rb:
devise_for :users, :controllers => {registrations: 'registrations'}, path_prefix: 'my'
resources :users
users/edit.html.haml
<%= bootstrap_form_for #user, html: {multipart: true} do |f| %>
...
<%= f.submit "Update" %>
users_controller.rb
def update
#user = User.find(params[:id])
Rails.logger.info(params.inspect)
if #user.update_without_password(account_update_params)
flash[:success] = "User updated by admin."
redirect_to #user
else
Rails.logger.info(#user.errors.inspect)
flash[:alert] = "User update by admin failed to save"
render 'edit'
end
private
def account_update_params
params.require(:user).permit(:email, :first_name, :last_name, :username, :dob,:city, :state, :zip, :password, :password_confirmation, :current_password)
end
end
user.rb
def update_without_password(params, *options)
params.delete(:password)
params.delete(:password_confirmation)
result = update_attributes(params, *options)
clean_up_passwords
result
end
Log file:
Started GET "/users/2/edit" for 127.0.0.1 at 2017-06-18 19:45:01 -0400
Processing by UsersController#edit as HTML
Parameters: {"id"=>"2"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
Rendering users/edit.html.erb within layouts/application
Rendered users/edit.html.erb within layouts/application (12.6ms)
Rendered layouts/_shim.html.erb (0.3ms)
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 1], ["LIMIT", 1]]
Rendered layouts/_sidenav.html.erb (1.0ms)
Rendered layouts/_nav_top.html.erb (0.4ms)
Rendered layouts/_notice.html.erb (0.3ms)
Rendered layouts/_footer.html.erb (0.2ms)
Completed 200 OK in 193ms (Views: 190.3ms | ActiveRecord: 0.3ms)
Started PATCH "/users/2" for 127.0.0.1 at 2017-06-18 19:45:04 -0400
Processing by UsersController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"SwkF8C+P6iExhc1ju91C9xFgaR5GSKwy8KFJUaBtHCTKzEXmPIKhaAFHlfMO+6u4/UOM0y2IAAIkpfTsuBOX6g==", "user"=>{"first_name"=>"Bobby", "last_name"=>"", "username"=>"", "dob"=>"", "gender"=>"", "address1"=>"", "address2"=>"", "state"=>"", "zip"=>"", "email"=>"test#test.com"}, "commit"=>"Update", "id"=>"2"}
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
<ActionController::Parameters {"utf8"=>"✓", "_method"=>"patch", "authenticity_token"=>"SwkF8C+P6iExhc1ju91C9xFgaR5GSKwy8KFJUaBtHCTKzEXmPIKhaAFHlfMO+6u4/UOM0y2IAAIkpfTsuBOX6g==", "user"=>{"first_name"=>"Bobby", "last_name"=>"", "username"=>"", "dob"=>"", "gender"=>"", "address1"=>"", "address2"=>"", "state"=>"", "zip"=>"", "email"=>"test#test.com"}, "commit"=>"Update", "controller"=>"users", "action"=>"update", "id"=>"2"} permitted: false>
(0.1ms) begin transaction
Authentication Exists (0.1ms) SELECT 1 AS one FROM "authentications" WHERE "authentications"."user_id" = ? LIMIT ? [["user_id", 2], ["LIMIT", 1]]
Authentication Exists (0.0ms) SELECT 1 AS one FROM "authentications" WHERE "authentications"."user_id" = ? LIMIT ? [["user_id", 2], ["LIMIT", 1]]
(0.0ms) rollback transaction
#<ActiveModel::Errors:0x007f9cf49d1758 #base=#<User id: 2, email: "test#test.com", created_at: "2017-06-18 16:05:51", updated_at: "2017-06-18 16:05:51", username: "", first_name: "Bobby", last_name: "", dob: nil, gender: "", address1: "", address2: "", city: nil, state: "", zip: "", admin: false, phone: nil>, #messages={:password=>["can't be blank"]}, #details={:password=>[{:error=>:blank}]}>
Rendering users/edit.html.erb within layouts/application
Rendered users/edit.html.erb within layouts/application (5.1ms)
Rendered layouts/_shim.html.erb (0.3ms)
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 1], ["LIMIT", 1]]
Rendered layouts/_sidenav.html.erb (0.9ms)
Rendered layouts/_nav_top.html.erb (0.5ms)
Rendered layouts/_notice.html.erb (0.3ms)
Rendered layouts/_footer.html.erb (0.2ms)
Completed 200 OK in 199ms (Views: 188.4ms | ActiveRecord: 0.7ms)
The message #messages={:password=>["can't be blank"] is pointing to the answer, but everything I've tried isn't removing this requirement. I thought that Devise's update_without_password method would work out of the box.
I've even tried this at the top in user.rb.
validates :password, presence: true, length: {minimum: 5, maximum: 120}, on: :create
validates :password, length: {minimum: 5, maximum: 120}, on: :update, allow_blank: true
Thanks for the help SO!
Check out your UsersController. You are using except on your paramsand then using account_update_params in the conditional. except doesn't exactly delete the key and value from the Hash.
This is a helpful SO answer that should fix your situation here .
If that doesn't help I would follow Devise's existing guide on how to achieve this:
Make sure you follow the Devise guide for allowing users to edit their account without providing a password. I'm not sure what you are trying to do in your User model, are you trying to overwrite the Devise method?
If you are trying to overwrite the Devise method you should look at the method itself:
def update_without_password(params, *options)
params.delete(:email)
super(params)
end
If you are using this method, you should probably override this method to protect other attributes you would not like to be updated without a password.
Change your RegistrationsController to the match the docs:
def update_resource(resource, params)
resource.update_without_password(params)
end
Then follow the remaining two steps and confirm your edited your view and routes.
If you need to make any changes authentication wise be sure to change the update_resource method.

Redirect after sign in with Devise 4

I know there are a ton of questions like this, and a few wiki articles, but nothing seems to work.
They all seem to end up in some sort of redirect loop or something else like that.
This is the last thing I did, based on these instructions:
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 after_sign_in_path_for(resource)
sign_in_url = new_user_session_url
if request.referer == sign_in_url
super
else
stored_location_for(resource) || request.referer || root_path
end
end
end
Yet this is what my logs shows happens when I sign in:
Started POST "/users/login" for ::1 at 2016-06-15 16:19:57 -0500
Processing by Devise::SessionsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"iGvdqYUdMgS/XXqTrh+XpW3PWQn473q/lm/lqsBeRcrcP+JRg==", "user"=>{"email"=>"abc#test.com", "password"=>"[FILTERED]", "remember_me"=>"0"}, "commit"=>"Login"}
User Load (4.1ms) SELECT "users".* FROM "users" WHERE "users"."email" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["email", "abc#test.com"], ["LIMIT", 1]]
(0.6ms) BEGIN
SQL (8.2ms) 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", 2016-06-15 21:19:57 UTC], ["last_sign_in_at", 2016-06-15 05:59:17 UTC], ["sign_in_count", 10], ["updated_at", 2016-06-15 21:19:57 UTC], ["id", 1546]]
(2.6ms) COMMIT
Redirected to http://localhost:3000/login
Completed 302 Found in 218ms (ActiveRecord: 15.6ms)
Started GET "/login" for ::1 at 2016-06-15 16:19:57 -0500
Processing by Devise::SessionsController#new as HTML
User Load (3.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1546], ["LIMIT", 1]]
Redirected to http://localhost:3000/login
Filter chain halted as :require_no_authentication rendered or redirected
Completed 302 Found in 7ms (ActiveRecord: 3.6ms)
Started GET "/login" for ::1 at 2016-06-15 16:19:57 -0500
Processing by Devise::SessionsController#new as HTML
User Load (2.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1546], ["LIMIT", 1]]
Redirected to http://localhost:3000/login
Filter chain halted as :require_no_authentication rendered or redirected
Completed 302 Found in 6ms (ActiveRecord: 2.5ms)
Started GET "/login" for ::1 at 2016-06-15 16:19:57 -0500
Processing by Devise::SessionsController#new as HTML
User Load (2.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1546], ["LIMIT", 1]]
Redirected to http://localhost:3000/login
Filter chain halted as :require_no_authentication rendered or redirected
Completed 302 Found in 5ms (ActiveRecord: 2.0ms)
Started GET "/login" for ::1 at 2016-06-15 16:19:57 -0500
Processing by Devise::SessionsController#new as HTML
User Load (3.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1546], ["LIMIT", 1]]
Redirected to http://localhost:3000/login
Filter chain halted as :require_no_authentication rendered or redirected
Completed 302 Found in 6ms (ActiveRecord: 3.0ms)
How do I redirect to the page the user is coming from after they have logged in (or registered) without getting this loop?
Edit 1
Based on the feedback from SsoulLess, this is what happens.
For starters, the infinite loop no longer happens - so that's good. However, it doesn't actually redirect to the right place.
When I add a binding.pry right into that after_sign_in_path_for(resource) method and explore my session hash, this is what I see:
[1] pry(#<Devise::SessionsController>)> session[:referer]
=> nil
[2] pry(#<Devise::SessionsController>)> session[:referrer]
=> nil
In theory, it should have a value, because of what I did. I was coming from a Question#Show view. Nothing to do with Users.
What else should store the previous path I am coming from before login?
Edit 2
Based on further suggestions from SsoulLess, this is what my code looks like now:
ApplicationController.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
#aplication_controller.rb
def authenticate_user!(resource=nil)
session[:referer] = request.fullpath
redirect_to(new_user_registration_path, success: 'create an account or log in.') unless user_signed_in?
end
def after_sign_in_path_for(resource)
#Redirect back the user to the last action he wanted to do
if session[:referer] =~ /\/users/
root_path
elsif session[:referer].nil?
root_path
else
session[:referer]
end
end
end
And this is my QuestionsController
class QuestionsController < ApplicationController
before_action :set_question, only: [:edit, :update, :destroy]
before_action :set_voting_question, only: [:vote_up, :vote_down]
before_action :authenticate_user!, except: [:index, :show]
..
..
end
This is what my log looks like when I am viewing a Question#Show and need to login.
Started POST "/users/login" for ::1 at 2016-06-18 19:12:03 -0500
Processing by Devise::SessionsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"chcksZ9AMvZKvzJec5IMqwDhA58dYlB9tQy7OSY9dqqWw==", "user"=>{"email"=>"abc#test.com", "password"=>"[FILTERED]", "remember_me"=>"0"}, "commit"=>"Login"}
User Load (3.6ms) SELECT "users".* FROM "users" WHERE "users"."email" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["email", "abc#test.com"], ["LIMIT", 1]]
(0.7ms) BEGIN
SQL (6.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", 2016-06-19 00:12:03 UTC], ["last_sign_in_at", 2016-06-15 21:19:57 UTC], ["sign_in_count", 11], ["updated_at", 2016-06-19 00:12:03 UTC], ["id", 1546]]
(0.8ms) COMMIT
Redirected to http://localhost:3000/
Completed 302 Found in 190ms (ActiveRecord: 11.1ms)
Started GET "/" for ::1 at 2016-06-18 19:12:03 -0500
Processing by QuestionsController#index as HTML
Rendering questions/index.html.erb within layouts/application
Rendered shared/_main_page_heading.html.erb (0.6ms)
It still doesn't return the user back to the Question#show page like I expect.
Edit 3
After trying what #sohair-ahmad suggested, it still doesn't work.
This is what I get when I drop a binding.pry in that method and explore the session object:
6: def after_sign_in_path_for(resource)
=> 7: binding.pry
8: session["user_return_to"] || root_path
9: end
[1] pry(#<Devise::SessionsController>)> session
=> #<ActionDispatch::Request::Session:0x007fd8baa9c078 ...>
[2] pry(#<Devise::SessionsController>)> session["user_return_to"]
=> nil
[3] pry(#<Devise::SessionsController>)> exit
Redirected to http://localhost:3000/
Completed 302 Found in 18434ms (ActiveRecord: 6.2ms)
The fundamental issue is that I can't find the attribute within session that stores the actual location before /login. If I could, then it would solve this.
Edit 4
Here is a Gist of my application_controller.rb and the log for the entire operation - https://gist.github.com/marcamillion/d7826b7289dc55bf4d33e688851bfc26
You will notice the first thing I did is I went to a Question#Show page. Then I logged out and it redirected me to /. Then from there, I went back to another question#show and then I logged in from there too. You will see the login process and then you will see the redirect to my root_path again. So it still isn't working, even after trying #SsoulLess's most recent updates.
Edit 5
After doing the latest suggestion from #SsoulLess, I get the functionality I would like for the most part....except when I go from /register -> /login and then I do login. It sends me back to /register and then keeps me in an infinite loop. See the logs below:
Started GET "/register" for 127.0.0.1 at 2016-06-25 23:45:38 -0500
Processing by Devise::RegistrationsController#new as HTML
Rendering devise/registrations/new.html.erb within layouts/application
Rendered devise/shared/_links.html.erb (2.1ms)
Rendered devise/registrations/new.html.erb within layouts/application (44.3ms)
Rendered shared/_navbar.html.erb (1.4ms)
Rendered shared/_footer.html.erb (0.3ms)
Completed 200 OK in 206ms (Views: 203.8ms | ActiveRecord: 0.0ms)
Started GET "/login" for 127.0.0.1 at 2016-06-25 23:45:42 -0500
Processing by Devise::SessionsController#new as HTML
Rendering devise/sessions/new.html.erb within layouts/application
Rendered devise/shared/_links.html.erb (1.5ms)
Rendered devise/sessions/new.html.erb within layouts/application (29.2ms)
Rendered shared/_navbar.html.erb (1.4ms)
Rendered shared/_footer.html.erb (0.5ms)
Completed 200 OK in 190ms (Views: 188.2ms | ActiveRecord: 0.0ms)
Started POST "/users/login" for 127.0.0.1 at 2016-06-25 23:45:48 -0500
Processing by Devise::SessionsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"cZ+kP5Q+rSuz3G/Jxwe0fPRSOnqJdA==", "user"=>{"email"=>"abc#test.com", "password"=>"[FILTERED]", "remember_me"=>"0"}, "commit"=>"Login"}
User Load (2.3ms) SELECT "users".* FROM "users" WHERE "users"."email" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["email", "abc#test.com"], ["LIMIT", 1]]
(0.8ms) BEGIN
SQL (1.4ms) 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", 2016-06-26 04:45:48 UTC], ["last_sign_in_at", 2016-06-26 04:45:04 UTC], ["sign_in_count", 27], ["updated_at", 2016-06-26 04:45:48 UTC], ["id", 1546]]
(0.8ms) COMMIT
Redirected to http://localhost:3000/register
Completed 302 Found in 168ms (ActiveRecord: 5.3ms)
Started GET "/register" for 127.0.0.1 at 2016-06-25 23:45:48 -0500
Processing by Devise::RegistrationsController#new as HTML
User Load (1.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1546], ["LIMIT", 1]]
Redirected to http://localhost:3000/login
Filter chain halted as :require_no_authentication rendered or redirected
Completed 302 Found in 4ms (ActiveRecord: 1.3ms)
Started GET "/login" for 127.0.0.1 at 2016-06-25 23:45:48 -0500
Processing by Devise::SessionsController#new as HTML
User Load (1.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1546], ["LIMIT", 1]]
Redirected to http://localhost:3000/login
Filter chain halted as :require_no_authentication rendered or redirected
Completed 302 Found in 4ms (ActiveRecord: 1.3ms)
This is the latest ApplicationController, how do I modify it so I don't get this issue:
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
after_action :store_location
def store_location
session[:previous_url] = request.referer
end
def after_sign_in_path_for(resource)
session[:previous_url] || root_path
end
end
You can use the session[:referer] it works with the latest version of Devise.
def after_sign_in_path_for(resource)
#Redirect back the user to the last action he wanted to do
if session[:referer] =~ /\/users/
root_path
elsif session[:referer].nil?
root_path
else
session[:referer]
end
end
Now as you can see above, if the referer is one of the /user/ page or the referee is nil, then we redirect to root_path otherwise we'll redirect to the referer, it means to the last action the user wanted to perform before log in.
Update
Yes Im sorry, I missed the part where you set the session[:referer] hash, you need to set it manually when getting in the log in page:
#aplication_controller.rb
def authenticate_user!(resource=nil)
session[:referer] = request.fullpath
redirect_to( new_user_registration_path, success: 'create an account or log in.') unless user_signed_in?
end
Update 2
the authenticate_user! method in your application controller is overriding the before_action :authenticate_user! filter, this is the way you use for request login for the actions within your controllers.
So when the user wants to perform an action he needs to log in, the before_action :authenticate_user! filter will redirect to the log in page, but the session[:referer] is set in the authenticate_user! function in your application controller. Then you can use it in the after_sign_in_path_for for redirect the user to the proper action the user wanted to perform before log in.
Update 3 - The final version that works! (almost, see below)
I understood what you want to achieve, you want to redirect the user back to the last page he visited before log in:
#aplication_controller.rb
after_action :store_location
def store_location
# store last url as long as it isn't a /users path
session[:previous_url] = request.referer
end
def after_sign_in_path_for(resource)
session[:previous_url] || root_path
end
Update 4 (this update is added by the OP)
For the most part, Update 3 worked, but there was one edge case that sent the app into an infinite loop. If the request.referer was one of the Devise actions upon login. See the updated question for a complete explanation of what happens.
Here is the complete solution that handles this edge case successfully:
def store_location
# store last url as long as it isn't a /users /register /login path
if request.referer =~ /\/users|\/login|\/register/
session[:previous_url] = root_path
elsif request.referer.nil?
session[:previous_url] = root_path
else
session[:previous_url] = request.referer
end
end
def after_sign_in_path_for(resource)
session[:previous_url] || root_path
end
I had the same issue and this A Simpler Solution? worked for me.
Adding snippet here
class ApplicationController < ActionController::Base
private
# If your model is called User
def after_sign_in_path_for(resource)
session["user_return_to"] || root_path
end
# Or if you need to blacklist for some reason
def after_sign_in_path_for(resource)
blacklist = [new_user_session_path, new_user_registration_path] # etc...
last_url = session["user_return_to"]
if blacklist.include?(last_url)
root_path
else
last_url
end
end
end

Devise ERROR: Auth token has already been taken

Trying to signup in my RoR webapp give me the Devise Message "Auth token has already been taken"
Also, the webapp have an API and works fine, doesn't give any message, this only happen when I'm trying to use the HTML view.
user_controller.rb
before_action :set_user, only: [:show, :edit, :update, :destroy]
# DELETE /users/:id.:format
def destroy
# authorize! :delete, #user
#user.destroy
respond_to do |format|
format.html { redirect_to root_url }
end
end
private
def set_user
#user = User.find(params[:id])
end
def user_params
accessible = [ :name, :email ]
accessible << [ :password, :password_confirmation ] unless params[:user][:password].blank?
params.require(:user).permit(accessible)
end
User.rb
validates :auth_token, uniqueness: true
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
before_create :generate_authentication_token!
def generate_authentication_token!
begin
self.auth_token = Devise.friendly_token
end while self.class.exists?(auth_token: auth_token)
end
logs
Started GET "/users/sign_up" for 127.0.0.1 at 2015-06-30 09:31:46 -0500
Processing by Devise::RegistrationsController#new as HTML
Rendered devise/registrations/new.html.haml within layouts/application (12.9ms)
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."deleted_at" IS NULL AND "users"."auth_token" IS NULL LIMIT 1
Rendered layouts/_navigation_links.html.haml (2.1ms)
Rendered layouts/_navigation.html.haml (3.4ms)
Rendered layouts/_messages.html.haml (0.2ms)
Completed 200 OK in 132ms (Views: 117.0ms | ActiveRecord: 1.5ms)
Started POST "/users" for 127.0.0.1 at 2015-06-30 09:32:00 -0500
Processing by Devise::RegistrationsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"20w9AXmACwggvPocKfLBdrxQRasT5OiaC7niuzooBBm3BAp8xkN6VLWyxZLRoLIpFPEIIdkxZRd9CCwsJxkeUA==", "user"=>{"email"=>"hola#x.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "commit"=>"Sign up"}
(0.1ms) BEGIN
User Exists (0.4ms) SELECT 1 AS one FROM "users" WHERE "users"."auth_token" = '' LIMIT 1
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = 'hola#x.com' LIMIT 1
(0.1ms) ROLLBACK
Rendered devise/registrations/new.html.haml within layouts/application (3.2ms)
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."deleted_at" IS NULL AND "users"."auth_token" IS NULL LIMIT 1
Rendered layouts/_navigation_links.html.haml (1.5ms)
Rendered layouts/_navigation.html.haml (2.1ms)
Rendered layouts/_messages.html.haml (0.2ms)
Completed 200 OK in 232ms (Views: 134.4ms | ActiveRecord: 1.2ms)
Started GET "/assets/jquery/jquery-bb5529929fa5581a780a38ecb7470f2c.js?body=1" for 127.0.0.1 at 2015-06-30 09:32:00 -0500
Follow the following
1) Open Rails console
rails console
2) Get the total count of users
user = User.all
user.count
this should be 1
3) Get the user and check the auth token
user = User.last
user.auth_token
auth token would be an empty string which is the reason your command is failing as the user doesn't have valid auth token
4) Create a valid auth token for the user
user.auth_token = Devise.friendly_token
user.save
It would create a valid auth token for the user and save it
5) Now you can run your commands and it would work perfectly
Cheers! :)
It's probably because you already have users in your db without auth_token,
use Devise.friendly_token to update those users with a token

Resources