I have decided to deal with sessions in my application on a cookie level, so I have a session controller that looks like:
module Xaaron
class SessionsController < ApplicationController
def new
end
def create
user = Xaaron::User.authenticate_user(params[:user_name], params[:password])
if sign_in(user)
if params[:remember_me]
cookies.permanent[:auth_token] = user.auth_token
else
cookies[:auth_token] = user.auth_token
end
flash[:notice] = "Welcome back, #{user.first_name}."
redirect_to root_path
else
flash[:alert] = "You have entered incorrect credentials."
redirect_to login_path
end
end
def destroy
cookies.delete(:auth_token)
redirect_to root_path
end
end
end
My application is kind of a "gate keeper" application so the user can login into say site.com and from there go to product1.site.com, their information such as user name, api key, all that jazz is shared between these two apps via promiscuous gem.
How ever my question is:
is the cookie created in site.com viable for use in product1.site.com thus allowing me to use specific helper methods such as: current_user in product1.site.com to see if said user is logged in?
The purpose for this is that if user A is not signed into site.com they cannot access product1.site.com
RFC 6265 has the answer in section 4.1.2.3. If the cookie domain attribute is set to dom.ain, then the cookie is sent by the user agent when making requests to dom.ain, www.dom.ain, sub.dom.ain, and other subdomains of dom.ain. You can control the cookie domain attribute via the domain key in the cookies hash, like this
cookies.signed[:secure_session] = {domain: 'dom.ain', value: "#{user.salt}#{user.id}"}
Related
I need to create functionality where other microservice creates a link to my app with JWE token as a params in which is encrypted json user params e.g.:
json_payload = {
email: 'test#test.com',
external_id: '1234'
}.to_json
The flow should be:
user gets the url generated by different app with JWE token as params (e.g. http://localhost:3000/users/sign_up/?jwe_token=some_gigantic_string_123)
enter that url
under the hood Rails app creates new user based on encrypted params
after successful user creation redirect that user to the edit page
So as you see, the user shouldn't notice that there was an account creation but the first page it will see is the password edit.
Am I not doing some sort of antipaternity here with below code? Please take a look:
class Users::RegistrationsController < Devise::RegistrationsController
# GET /resource/sign_up
def new
return redirect_to(new_user_session_path) unless params[:jwe_token]
json_payload = JWE.encrypt(payload, rsa_key)
payload = JSON.parse json_payload
user = User.new(user_params)
if user.save
redirect_to generate_password_url(request.base_url, user)
else
redirect_to new_user_session_path, alert: 'Something went wrong'
end
end
private
def generate_password_url(base_url, user)
path = edit_password_path(user, reset_password_token: fetch_token(user))
"#{base_url}#{path}"
end
def fetch_token(user)
user.send(:set_reset_password_token)
end
end
I assume that if user creation is to be handled by a link I have to use new method. Am I not creating an antipattern here? Is there any other way to do so?
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 have a method that creates a new user session and will redirect back to the previous screen after successful login. It has introduced a security issue where someone can type in a url navigate to a page they shouldn't have access to. The app will direct them to login, but after logging with valid credentials (just not the proper level of clearance) it will redirect them to the page they had manually typed into the url. How can I verify that redirect_back isn't sending a user to a page they shouldn't have access to?
Here is the session create method:
def create
#user = authenticate(params)
sign_in(#user) do |status|
if status.success?
redirect_back root_path
else
flash.now.alert = status.failure_message
render :new, status: :unauthorized
end
end
end
Is there a way to see what address it will be sending them back to because I could just do something like
if back_url.includes? "admin"
redirect_to root_path
end
You can’t rely on URL obscurity for security of your application. Your admin routes or controllers should be protected so they can only be accessed by signed in admins.
You can do this with a route constraint in your routes file (my preference) or a before action that is consistently applied across all of your admin controllers that returns a 403 if the current user is not an admin.
I have a user who can login to my Ruby on Rails application. I wrote the functions myself with an sessions controller. I also set a time in session_store.rb when the session should expire.
This all works fine, But additionally I want to track when users login or logout. Right now I can trace the login and logout when it's done manually by the user. But when a session expires my "sessions#destroy" option is not triggered and therefore no logout time is set.
Is there any possibility to trigger an action when the session expires or when a session is killed by rails?
Thanks!
UPDATE:
Sorry, I should have included some of my code:
app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
def new
if !current_user.nil?
redirect_to start_path
end
end
def create
user = User.find_by_username(params[:username])
if user && user.authenticate(params[:password])
session[:user_id] = user.id
UserTracing.create(user_id: user.id, sign_in_at: DateTime.now)
redirect_to start_path, notice: I18n.t("text logged_in")
else
flash.now.alert = I18n.t("error username_or_password_invalid")
render "new"
end
end
def destroy
#id = current_user.id
session[:user_id] = nil
UserTracing.where(user_id: #id).order(sign_in_at: :asc).last.update_attributes(sign_out_at: DateTime.now)
redirect_to root_url, notice: I18n.t("text logged_out")
end
end
This is easy.
All you have to do is use your sessions helper method to
Create a time when the session is set to expire
sessions[:expires_at] = Time.now + minutes_to_run * 60
Create a function that retrieves this time.
Have a function in your controller that is run before this page is loaded to check if the current time is greater than the time the session was set to expire. The solution uses javascript setTimeout() to ensure the page is refreshed otherwise you would have to wait for the user to reload. This way your app refreshes the page.
Here's a blog post that details this idea.
https://medium.com/#thatlovelypract/rubyonrails-timed-session-a5de0cf70f3b
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