Navigating within the app and re-directs are all fine, its just when the browser refreshes the user has to log back in. I only want the session to expire when the browser closes and not on refresh..
My session_store.rb
Rails.application.config.session_store :cookie_store, key:
'_workspace_session'
My sessions controller new and create actions:
def new
end
def create
merchant = Merchant.find_by(email: params[:email])
if merchant && merchant.authenticate(params[:password])
session[:merchant_id] = merchant.id
log_in(merchant)
flash[:success] = "You were logged in successfully"
redirect_to merchant_path(merchant.id)
else
flash.now[:danger] = "Snap! either your email or password is
incorrect. Try again"
render 'new'
end
end
It's probably because you don't have a sessions helper that adds a cookie of the logged in merchant's credentials and checks them every time you reload the page:
First, add your sessions_helper.rb to your main controller:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
include SessionsHelper
end
Here is your sessions helper:
module SessionsHelper
# Returns the user corresponding to the remember token cookie.
def current_merchant
if (merchant_id = session[:merchant_id])
#current_merchant ||= Merchant.find_by(id: merchant_id)
elsif (merchant_id = cookies.signed[:merchant_id])
merchant= Merchant.find_by(id: merchant_id)
if merchant && merchant.authenticated?(cookies[:remember_token])
log_in merchant
#current_user = merchant
end
end
end
#logs the merchant in.I don't know what your log_in method does there in the question, if it does the same, you are doing it two times.
def log_in(merchant)
session[:merchant_id] = merchant.id
end
end
Adapted from RailsTutorial <- I'd recommend you read this. A lot of great stuff in there.
Related
This question already has answers here:
Session not destroyed when closing browser - RailsTutorial.org
(2 answers)
Closed 5 years ago.
It's my understanding that the default behavior of Rails, when storing a session e.g. session[:id] = 1, is that it will save the id to a cookie, which will expire when the user closes the browser window. However, in my app, when I close (exit out) the browser and restart it, the browser still 'remembers' me as being logged in. Here is my controller code:
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by(email: params[:email])
if user && user.authenticate(params[:password])
log_in user
redirect_to user
else
flash.now[:notice] = "Invald email / password combination"
render 'new'
end
end
def destroy
end
end
and my helper file:
module SessionsHelper
def log_in(user)
session[:user_id] = user.id
end
def current_user
#current_user ||= User.find_by(id: session[:user_id])
end
def logged_in?
!current_user.nil?
end
def user_name
#current_user.first_name ? (#current_user.first_name + " ") : nil
end
end
I have nothing in the application controller nor did I ever mess with the initializers or config files regarding the session. What could be causing my session to persist and not expire the cookie?
Withing config/initializers/session_store.rb file add
Rails.application.config.session_store :cookie_store, key: '_app_session', expire_after: nil
Instead of
Rails.application.config.session_store :cookie_store, key: '_app_session'
Please have a look at here for more info.
The solution is to simply turn off cookies or reset cookies, which results in a new session being created with each visit. The solution looked like this:
On client side, write a beforeunload event handler.
From that handler make an AJAX request to logout URL, in your case it
might be a special view that deletes the cookie (see Rails docs on
cookies).
The AJAX request has to be synchronous to make sure beforeunload
event handler waits for its completion.
refer this https://codedecoder.wordpress.com/2013/12/04/clear-session-or-cookie-browser-close-ruby-jquery/ it might also help
I have a Rails 5 app with Devise. Each user has a role_id where they are assigned a role upon creation. I'm trying to use the after_sign_in_path_for method that Devise gives to redirect to a specific page on login based on the role.
Below is what I have so far, but it doesn't work when trying to sign out a disabled user.
class ApplicationController < ActionController::Base
def after_sign_in_path_for(resource)
case resource.role_id
when Role.find_by(name: "admin").id
root_path
when Role.find_by(name: "disabled").id
destroy_user_session_path
else
super
end
end
end
I'm able to sign in when I'm an admin user and it redirects. But if I try to sign in as a user whose role is disabled, it tries to tear down the session then raises an exception of No route matches [GET] "/users/sign_out". I know the method destroy_user_session_path expects a delete method but how can I pass this in the application controller?
What am I doing wrong here?
Update
I tried the sign_out(resource) as suggested in the first answer, and it raises an exception undefined methodto_model' for true:TrueClassin mymy_sessions_controller.rb` which I use to override the create method to set a login token and limit concurrent sessions. Here is the controller.
class MySessionsController < Devise::SessionsController
skip_before_action :check_concurrent_session
def create
super
set_login_token
end
private
def set_login_token
token = Devise.friendly_token
session[:token] = token
current_user.login_token = token
current_user.save(validate: false)
end
end
You can check roles inside MySessionsController#create and prevent logging if the role not valid instead of allowing user to login then logout
def create
unless current_user.role_id == Role.find_by(name: "disabled").id
super set_login_token
else
redirect_to new_user_session_path, alert: "You can't log in"
end
end
You can also use active_for_authentication? and inactive_message methods in user model to prevent him from login. in /app/models/user.rb:
def active_for_authentication?
super and self.role_id != Role.find_by(name: "disabled").id
end
def inactive_message
"You can't log in"
end
destroy_user_session_path is making [GET] "/users/sign_out" request.
You can use sign_out or reset_session function to delete session directly.
Hope this answer works for you.
Use devise's sign_out(resource) method instead of destroy_user_session_path. This method will destroy the user session.
Is it possible to force logout for SOME of the users through Devise.
In my setup, I am using rails 4.1.15 with Devise 1.5.4. Sessions are persisted in db and there is no direct mapping with user ids. Is there a Devise way to logout against some of the users NOT ALL.
I tried resetting password as a proxy which logs out immediately but not always.
user_obj.update_attributes(:password => "some_random_string")
I can propose you next solution.
Add a new column for admin that called force_logout:boolean
In any your controller add a new action to set force_logout to true. Ex.:
# in admins_controller.rb
def force_logout
admin = Admin.find(params[:id])
admin.update_column(:force_logout, true)
redirect_to :back
end
In application_controller.rb add before_action to logout user if force_logout is true
before_action :check_force_logout
def check_force_logout
if current_user && current_user.force_logout?
current_user.update_column(:force_logout, false)
sign_out(current_user)
end
end
Too you need reset force_logout column after admin will be signed in. Usually you can do it session_controller.rb in action create.
Time to exhume this one. The only way I was able to solve this issue was by accessing the sessions. It is slow but it works.
def sign_out_different_user
#user = User.find(params[:id])
sessions = ActiveRecord::SessionStore::Session.where("updated_at > ?", Time.now - 480.minutes).all
sessions.each do |s|
if s.data['user_email'] == #user.email
s.delete and return
end
end
end
I needed to change the session expiration on the user object for my purposes, so I added this into the method, as well. YMMV
#user.session_expires_at = Time.now
#user.save
I want to know how I can make specific user(not current_user) sign out.
I saw this http://www.rubydoc.info/github/plataformatec/devise/master/Devise/Controllers/SignInOut#sign_out-instance_method and maked this code.
def kick_admin
user = User.find params[:user_id]
user.admin = false
user.save
sign_out user #want to kick him.
end
But it does not make that user sign out but make me(current_user) signed out.
What is the right way to use the sign_out method?
I checked this answer(Sign out specific user with Devise in Rails) but it was not helpful.
One way you could do this is create a new attribute in the User table, call it force_sign_out.
def kick_admin
user = User.find params[:user_id]
user.update_attributes(admin: false, force_sign_out: true)
end
And have a before action in ApplicatonController so that if the user attempts any activity he's signed out
class ApplicationController < ActionController::Base
before_action :check_if_force_sign_out
def check_if_force_sign_out
return unless current_user.force_sign_out
current_user.update_attributes(force_sign_out: false) # reset for non-admin log in
sign_out
redirect_to root_path
end
end
I'm trying to set up gradual engagement in my utility app which people can use without registering e.g. notepad.cc and jsfiddle.net and I plan to create a guest user (with Devise) for the user when he 'writes' to the app.
I found this guide on the Devise wiki https://github.com/plataformatec/devise/wiki/How-To:-Create-a-guest-user which shows how to create a guest user for the duration of the browser session. What I want is for the user to continue using the same guest account in subsequent visits, until he signs up, maybe when I introduce subscription plans for more features.
How can I modify what's in the guide to make this possible?
Code in the guide linked above:
# file: app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
# if user is logged in, return current_user, else return guest_user
def current_or_guest_user
if current_user
if session[:guest_user_id]
logging_in
guest_user.destroy
session[:guest_user_id] = nil
end
current_user
else
guest_user
end
end
# find guest_user object associated with the current session,
# creating one as needed
def guest_user
User.find(session[:guest_user_id].nil? ? session[:guest_user_id] = create_guest_user.id : session[:guest_user_id])
end
# called (once) when the user logs in, insert any code your application needs
# to hand off from guest_user to current_user.
def logging_in
end
private
def create_guest_user
u = User.create(:name => "guest", :email => "guest_#{Time.now.to_i}#{rand(99)}#email_address.com")
u.save(false)
u
end
end
And using it in the controller:
#thing.user = current_or_guest_user
#thing.save
After some yak-shaving I've managed to get it to work. Here's the working code:
class ApplicationController < ActionController::Base
protect_from_forgery
# if user is logged in, return current_user, else return guest_user
def current_or_guest_user
if current_user
if cookies[:uuid]
logging_in # Look at this method to see how handing over works
guest_user.destroy # Stuff have been handed over. Guest isn't needed anymore.
cookies.delete :uuid # The cookie is also irrelevant now
end
current_user
else
guest_user
end
end
# find guest_user object associated with the current session,
# creating one as needed
def guest_user
User.find_by_lazy_id(cookies[:uuid].nil? ? create_guest_user.lazy_id : cookies[:uuid])
end
# called (once) when the user logs in, insert any code your application needs
# to hand off from guest_user to current_user.
def logging_in
# What should be done here is take all that belongs to user with lazy_id matching current_user's uuid cookie... then associate them with current_user
end
private
def create_guest_user
uuid = rand(36**64).to_s(36)
temp_email = "guest_#{uuid}#email_address.com"
u = User.create(:email => temp_email, :lazy_id => uuid)
u.save(:validate => false)
cookies[:uuid] = { :value => uuid, :path => '/', :expires => 5.years.from_now }
u
end
end
I will accept another answer if you can show me a better way to do this.
The above solution works great.
Don't forget to setuphelper_method :current_or_guest_user to make the method accessible in views. Took me some time to figure out.