Ruby: 3.1.3
Rails Version: 6.1.6.1
Chrome Version: 109.0.5414.119
Setting a user_id in a cookie in a session controller, but it's not showing up on subsequent requests, and looking at the application inspector in chrome it doesn't show any cookies set.
This is the controller and function that sets the cookie
class Api::Auth::UserController < ApplicationController
def create_session
user = User.find_by(email: params[:email])
if user.present?
head :unauthorized unless user.authenticate(params[:password])
session[:current_user_id] = user.id
#success = true
debugger
else
head :unauthorized
end
end
No error message other than the 401 unauthorized I throw when session[:current_user_id] is nil.
Works perfectly on firefox, but for some reason Chrome doesn't persist the cookie that tries to get set.
Better to use cookies instead of session, by the documentation at https://api.rubyonrails.org/classes/ActionDispatch/Cookies.html
you should be able to do it with:
cookies[:current_user_id] = user.id
You can also use encryption to store the value if you think this should be protected.
There are several leads for you to investigate why Chrome is not working:
Timeout of the session
SameSite (check https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite)
Beta versions of chrome sometimes have problems or introduce new experimental features.
Related
Would it possible to set a cookie in a patched version of devise failure app redirect with the attempted path value(the relative path of the protected page the user was attempting to access)?
Example Attempt:
def redirect
store_location!
if is_flashing_format?
if flash[:timedout] && flash[:alert]
flash.keep(:timedout)
flash.keep(:alert)
else
flash[:alert] = i18n_message
end
end
# Example Patch:
cookies["some-key"] = attempted_path # <--- patched code!
# binding.pry I can see the cookie set in request.cookies here
redirect_to redirect_url # but once redirected_to(some domain page) it isn't set on new page
end
When I attempt to set the cookie, it does not persist after the redirect(same domain), although if I put a breakpoint right afterwards(before the #redirect_to), I can see it in the request.cookies object. I don't believe it is a CSRF issue because I can access the existing cookies despite having null session protection configured. I think I'm fundamentally missing something here...
I've been battling this for about 24 hours now, and nothing I'm finding in my searches is leading to a solution.
My issue is my session data is not persisting and I can not log in to my app. Everything worked in Dev mode, but has not yet worked in Production. I'm using a Rails 6 Api hosted on Heroku and a React front end. I can successfully make the api call, find the user, and log them in using (I use "puts" to help me log the session at that instance. The session hash has a session_id and user_id at this point):
def login!
session[:user_id] = #user.id
puts "login_session: #{session.to_hash}"
end
After this the app redirects to the user page or an admin page depending on the users authorization.
When the redirect happens that the user or admin page calls the api to see if the user is authorized using:
def logged_in?
puts "logged_in_session: #{session.to_hash}"
!!session[:user_id]
end
The session is empty. Here is my sessions controller:
class SessionsController < ApplicationController
def create
#user = User.find_by(email: session_params[:email])
puts #user.inspect
if #user && #user.authenticate(session_params[:password])
login!
render json: {
logged_in: true,
user: UserSerializer.new(#user)
}
else
render json: {
status: 401,
errors: ['no such user', 'verify credentials and try again or signup']
}
end
end
def is_logged_in?
if logged_in? && current_user
render json: {
logged_in: true,
user: UserSerializer.new(current_user)
}
else
render json: {
logged_in: false,
message: 'no such user or you need to login'
}
end
end
def is_authorized_user?
user = User.find(params[:user_id][:id])
if user == current_user
render json: {
authorized: true
}
else
render json:{
authorized: false
}
end
end
def destroy
logout!
render json: {
status: 200,
logged_out: true
}
end
def omniauth
#user = User.from_omniauth(auth)
#user.save
login!
render json: UserSerializer.new(#user)
end
private
def session_params
params.require(:user).permit(:username, :email, :password)
end
def auth
request.env['omniauth.auth']
end
Would any be able to point me the right direction??
Thank you
I would verify the following:
When first authenticated, does the response from the endpoint include the cookie data?
Check the cookie store in your browser (there's a few extensions you can use to make this easier) and verify that the domain names match and the content in the cookie is what you'd expect.
You can cross reference the cookie ID with the ID in your session store (depending on where you've chosen to store this).
Can you verify the cookie contents (user_id) and session contents in the session store.
Make sure that the cookie data is being sent on the next request after authenticating (check the request headers in the network tab of your dev tools in the browser).
This is all assuming that you're using a browser to talk to this JSON endpoint. APIs usually don't use cookies as it's a browser thing. Alternative authentication mechanisms might be a short lived token (JWT for example) that is generated when authenticating that can be used for subsequent requests.
Quick update: I am able to get the "Set-Cookie: _session_id=..." in the response but it is blocked to due to "SameSite=lax" attribute.
I believe I need to change to SameSite = none, but I'm not sure were to do that.
Any advice?
A bit late but if you're using Rails 6 API, session has been disabled. You need to add the middleware manually. Here is the documentation using-session-middlewares
# This also configures session_options for use below
config.session_store :cookie_store, key: '_interslice_session'
# Required for all session management (regardless of session_store)
config.middleware.use ActionDispatch::Cookies
config.middleware.use config.session_store, config.session_options
I'm building an API for a web app I'm developing, and the following code I'm trying to use for API authentication/login is returning false on the authorization.
In my API user controller I have:
def login
if params[:user]
# Find the user by email first
#user = User.where(email: params[:user][:email]).first
if !#user
respond_with nil
else
#auth = #user.authenticate(params[:user][:password])
if #auth
respond_with #user
else
respond_with #auth
end
end
end
end
It is always responding with #auth, which is false, even when valid email and passwords are being provided. It has no problem pulling the user info from my Mongo db.
I guess I'm just not clear on what .authenticate does. According to a railscast.com video I watched, it should compare that users password digest with the password entered. When a valid password is provided for the user, #auth is always false.
This method was actually working fine, the test data in the database wasn't what i thought it was..
In my rails 3 app I use Omniauth for the user authentication part (fb/twitter).
Actually I follow this:
https://github.com/RailsApps/rails3-mongoid-omniauth
https://github.com/RailsApps/rails3-mongoid-omniauth/wiki/Tutorial
But,
when I close the browser session expires and I need to login again.
How can I keep the session for returning users?
Any help would be greatly appreciated!
What you want is not difficult, you only have to set a permanent cookie when the session is created and then retrieve this value when you set the current user.
In your ApplicationController, just change your current_user method to:
def current_user
return unless cookies.signed[:permanent_user_id] || session[:user_id]
begin
#current_user ||= User.find(cookies.signed[:permanent_user_id] || session[:user_id])
rescue Mongoid::Errors::DocumentNotFound
nil
end
end
And in your SessionsController, modify your create to set the cookie if user wants to:
def create
auth = request.env["omniauth.auth"]
user = User.where(:provider => auth['provider'],
:uid => auth['uid']).first || User.create_with_omniauth(auth)
session[:user_id] = user.id
cookies.permanent.signed[:permanent_user_id] = user.id if user.really_wants_to_be_permanently_remembered
redirect_to root_url, :notice => "Signed in!"
end
Devise offers this functionality through its Rememberable module. OmniAuth integrates easily with it through the (you'd never guess it) OmniAuth module. It's even mentioned in the second link you posted!
Please make sure the cookie policy that your rails app follows does have sensible settings for your use case (see the link in my comment above). All I can imagine right now (knowing what I know, sitting where I sit) is that the cookie(s) ha(s/ve) properties that are suboptimal/undesirable in your context.
Please check the cookie settings in a browser debug/development tool such as firebug, firecookie or the chrome development tools.
Sorry, that's all I can come up with given my knowledge of the problem. Feel free to contact me again with more details on your cookie- and testing-setup.
My 2Cents.
Here is how it works for me:
def google_oauth2
#user = User.from_google(google_params)
if #user.persisted?
#user.remember_me = true
sign_in #user
........
end
end
There is another way to do it
I have a RoR application that's using the RESTful Authentication plug-in. Everything works great. I recently enabled cookie based authentication and that works fine too. The problem is that I want to change the default landing page when the user is authenticated using a cookie. I want to have a cookie authenticated user redirected to the same page they are redirected to upon successful login from the login form. They are always directed to the original request URL. I'm racking my brain on this as I thought I understood how it works and every change I make seems to have no impact.
I suspect this is something simple but I'm obviously missing it. I'd appreciate any feedback, guidance or suggestions you might offer.
I solved the problem but it's a bit ugly in my opinion. Here's what I did.
In the cookie authentication method I set a session variable indicating the cookie login method was used.
def login_from_cookie
user = cookies[:auth_token] && User.find_by_remember_token(cookies[:auth_token])
if user && user.remember_token?
session[:cookie_login] = true **# this is my addition**
self.current_user = user
handle_remember_cookie! false # freshen cookie token (keeping date)
self.current_user
end
end
Then in the :before_filter set_current_user I just check for that variable and redirect if it is set making sure to set the variable to nil.
def set_current_user
Authorization.current_user = current_user
if session[:cookie_login]
redirect_to :controller => :users, :action => :search
session[:cookie_login] = false
end
end
It's not pretty but it does work. I'm definitely open to any suggestions about how to clean this up.
You could add this line to the session controller after a successful login:
redirect_to :controller => 'dashboard', :action => 'index'
I'm using Bort so maybe this isn't part of Restful_Authentication itself but there is a successful_login method in the sessions controller that uses this restful_auth method:
redirect_back_or_default( root_path )
which is in defined in authenticated_system.rb
def redirect_back_or_default(default)
redirect_to(session[:return_to] || default)
session[:return_to] = nil
end
Can't you just have your routes setup so that
map.root :controller => :users, :action => :search
And then have a before_filter that checks to make sure that some "logged in" parameter is set? This param would just need to be set whenever the user logs in, either via cookie or via normal means. Then, whether the cookie authentication happens or normal auth happens, it will go to the default page. Maybe I'm misunderstanding the problem.
Restful Authentication stores the original URL that was trying to be accessed when the request is made. All of you have to do is prevent it from storing that value OR clear that value when a cookie authentication is performed and then the user will get redirected back to your default page.
I would probably do it like this in authenticated_system.rb
def login_from_cookie
user = cookies[:auth_token] && User.find_by_remember_token(cookies[:auth_token])
if user && user.remember_token?
self.current_user = user
session[:return_to] = nil # This clears out the return value so the user will get redirected to the default path
handle_remember_cookie! false # freshen cookie token (keeping date)
self.current_user
end
end
The is session[:return_to] = nil
Then just make sure you have set your default path in your sessions controller and you should be all set. The code in your sessions controller should be something like this:
redirect_back_or_default(the_path_you_want_to_send_them_to)