rails gives wrong session cookie to user - ruby-on-rails

We are using omniauth with facebook login in a Rails 3.2.13 app. It has pretty standard boilerplate code in the sessions_controller to manage user sessions. It uses the CookieStore for sessions. Here is the sessions_controller:
class SessionsController < ApplicationController
skip_authorization_check
def new
redirect_to '/auth/facebook'
end
def create
auth = request.env["omniauth.auth"]
user = User.where(:provider => auth['provider'],
:uid => auth['uid'].to_s).first || User.create_with_omniauth(auth)
# Reset the session after successful login, per
# 2.8 Session Fixation – Countermeasures:
# http://guides.rubyonrails.org/security.html#session-fixation-countermeasures
reset_session
session[:user_id] = user.id
user.add_role :admin if User.count == 1 # make the first user an admin
if user.email.blank?
redirect_to edit_user_path(user), :alert => "Please enter your email address."
else
redirect_to root_url, :notice => 'Signed in!'
end
end
def destroy
reset_session
redirect_to root_url, :notice => 'Signed out!'
end
def failure
redirect_to root_url, :alert => "Authentication error: #{params[:message].humanize}"
end
end
The other day a member of our team was testing our production version and went to go sign in to the app. When she browsed to the app she found she was already signed in as another user who had never used that computer or in fact ever been in our building. Based on what she experienced and our subsequent analysis it seems that she was given a session cookie for that user by the app. After much research I don't understand how that could happen i.e. the rack/rails framework could give a session cookie to the wrong user. Has anyone seen this before or heard of it happening? Any suggestions on how to debug this or where to put logging to get more insight into what could be going wrong?

Related

Using same email adress to login Rails app through Oauth and Sorcery

I use Sorcery gem in my Rails app. Oauth authentication is working on Google and Github services. But if user has same emails to login to Google and Github, my application ignores other attempt to login, because the used email already stored in database.
So, I need multiple login in my app through Oauth, even if emails in different services is equal. What should I do?
You can do it like this:
put it in ./app/controller/oauths_controller.rb
def callback
provider = auth_params[:provider]
if #user = login_from(provider)
redirect_to root_path, :notice => "Logged in from #{provider.titleize}!"
else
begin
#user = create_from(provider)
reset_session # protect from session fixation attack
auto_login(#user)
redirect_to root_path, :notice => "Logged in from #{provider.titleize}!"
rescue
provider_hash = sorcery_fetch_user_hash(provider)
user_email = provider_hash[:user_info]['email']
#user = User.find_by_email(user_email)
#user.authentications.create!(:provider => provider, :uid => provider_hash[:uid])
reset_session
auto_login(#user)
redirect_to root_path, :notice => "Logged in from #{provider.titleize}!"
rescue
redirect_to root_path, :alert => "Failed to login from #{provider.titleize}!"
end
end
end

Devise and Omniauth

I'm trying to integrate Devise with Omniauth and have some troubles with them both. The main problem is to bind Devise User model with Omniauth authentications. I want a simple way to associate my user, with external providers like facebook, twitter, g+ and so on.
One of the most annoying issues that arise with my application is:
If an user registered on my site with devise (I call it local user), that means, provided an email and a password, when user tries to login with twitter, the system asks for mail confirmation. If that mail already exists, then user have to provide another mail. I want instead that he can confirm with a password that it is actually his email. How to do that? How can I override that template?
This is my authentications controller:
class AuthenticationsController < ApplicationController
def index
#authentications = Authentication.all
end
def create
#authentication = Authentication.new(params[:authentication])
if #authentication.save
redirect_to authentications_url, :notice => "Successfully created authentication."
else
render :action => 'new'
end
end
def destroy
#authentication = Authentication.find(params[:id])
#authentication.destroy
redirect_to authentications_url, :notice => "Successfully destroyed authentication."
end
def twitter
omni = request.env["omniauth.auth"]
authentication = Authentication.find_by_provider_and_uid(omni['provider'], omni['uid'])
if authentication
flash[:notice] = "Logged in Successfully"
sign_in_and_redirect User.find(authentication.user_id)
elsif current_user
token = omni['credentials'].token
token_secret = omni['credentials'].secret
current_user.authentications.create!(:provider => omni['provider'], :uid => omni['uid'], :token => token, :token_secret => token_secret)
flash[:notice] = "Authentication successful."
sign_in_and_redirect current_user
else
user = User.new
user.apply_omniauth(omni)
if user.save
flash[:notice] = "Logged in."
sign_in_and_redirect User.find(user.id)
else
session[:omniauth] = omni.except('extra')
p session
redirect_to new_user_registration_path
end
end
end
end
I also have no idea where new_users_registration_path is.

cookie persistence with omniauth and linkedin gem

I'm running a rails application that lets users successfully authenticate with LinkedIn and import their LinkedIn profile data. The (big) problem I'm having is that the cookie data associated with one user that signs in first persists even after they sign out, and is pulled in for another separate user after they authenticate through LinkedIn. The first user's data overwrites the second user's data...big problem.
Help is very much appreciated!
Here is my sessions_controller:
class SessionsController < ApplicationController
def new
end
def create
if env['omniauth.auth']
user = User.from_omniauth(env['omniauth.auth'])
session[:user_id] = user.id
redirect_to auth_path
flash[:success] = 'Signed in with LinkedIn.'
else
user = User.find_by_email(params[:session][:email])
if user && user.authenticate(params[:session][:password])
sign_in user
redirect_back_or user
flash[:success] = 'Signed in the old-fashioned way.'
else
flash.now[:error] = 'Invalid email/password combination'
render 'new'
end
end
end
def destroy
cookies.delete(:remember_token)
session[:user_id] = nil
redirect_to root_path
end
end
I was having the exact same issue. Somewhere in your omniauth configurations there should be a path configuration for where the user is redirected to.
Before
https://api.linkedin.com/uas/oauth/authenticate
After - This fixed everything for me and made the controller action always require an authorization when executed so that new users on the same computer would not automatically use last user's LinkedIn cookie.
https://www.linkedin.com/uas/oauth/authorize

OmniAuth Facebook expired token error

I am using OmniAuth to get access to Facebook in my app. I am using the fb_graph gem: https://github.com/nov/fb_graph to post to Facebook. I am running omniauth-0.3.0 on Heroku for this app. The token that I save when the user is created is changed when the user logs in sometime later.
Code for creating user
class SessionsController < ApplicationController
def create
auth = request.env["omniauth.auth"]
user = User.find_by_provider_and_uid(auth["provider"], auth["uid"])||
User.create_with_omniauth(auth)
session[:user_id] = user.id
redirect_to root_url, :notice => "Signed in!"
end
The User model is:
def self.create_with_omniauth(auth)
create! do |user|
user.provider = auth["provider"]
user.uid = auth["uid"]
user.name = auth["user_info"]["name"]
user.token = auth["credentials"]["token"]
end
end
I am now seeing this error on about 30% users-
FbGraph::InvalidToken (OAuthException :: Error validating access token: Session does not match current stored session. This may be because the user changed the password since the time the session was created or Facebook has changed the session for security reasons.)
I saw that the expired token issue has been recently fixed in OmniAuth:
https://github.com/soopa/omniauth/commit/67bdea962e3b601b8ee70e21aedf5e6ce1c2b780
I used this code which tries to refresh the access token. However, I still get the same error. Can someone point to what I am missing? Is there some other way I could update the token every time the user logs in?
The only solution which has worked is to create a new User everytime the User logs in (I don't like this solution at all):
def create
auth = request.env["omniauth.auth"]
user = User.create_with_omniauth(auth)
session[:user_id] = user.id
redirect_to root_url, :notice => "Signed in!"
end
Thanks!
You can simply update the token when you create the session.
class SessionsController < ApplicationController
def create
auth = request.env["omniauth.auth"]
user = User.find_by_provider_and_uid(auth["provider"], auth["uid"]).tap do |u|
u.update_attributes(:token => auth["credentials"]["token"]) if u
end || User.create_with_omniauth(auth)
session[:user_id] = user.id
redirect_to root_url, :notice => "Signed in!"
end
I was using similar solution before you answered this question-
class SessionsController < ApplicationController
def create
auth = request.env["omniauth.auth"]
user = User.find_by_provider_and_uid(auth["provider"], auth["uid"]) || User.create_with_omniauth(auth)
user.update_attributes(:token => auth["credentials"]["token"])
session[:user_id] = user.id
redirect_to root_url, :notice => "Signed in!"
end
Can't we refresh the token using FBGraph gem with follwing method in such case?
auth = FbGraph::Auth.new(CLIENT_ID, CLIENT_SECRET)
auth.exchange_token! access_token # Needs fb_graph 2.3.1+
auth.access_token # => new token
However, This will not extend token's expiry but will replace token with new one. Expiry time will remain same. Checked it with FB, They may not allow to extend FB token expiry more than 60 days.
Maximum token validity is 60-days.
reference: https://github.com/nov/fb_graph/wiki/Authentication#wiki-extend-token-expiry

rails - email activation upon user signup

I want the user to click on an activation link before being "activated" or before they can log in with the email/password.
I am not using an gems and want to keep it that way. My problem is that after the user registers, they can login in without clicking on the activation code. I have an confirmation_token line and a confirmed line to the model.
user controller:
def create
#user = User.new(params[:user])
if #user.save
render "root_path"
else
render "new"
end
end
def confirmed
user = User.find(:first, :conditions => {:confirmation_token => params[:confirmation_token]})
if (!params[:confirmation_token].blank?) && user && !user.confirmed?
user.confirmed!
self.current_user = user
flash[:notice] = "Thank you. You account is now activated."
redirect_to account_preference_path(current_user)
else
flash[:notice] = "Sorry we don't have your email in our database."
redirect_to root_path
end
end
user model:
def confirmed!
self.confirmed = true
self.confirmation_token = nil
save(false)
end
Am I missing anything? Thanks!
I know there are gems like devise, auth-logic, etc out there but I want to learn how to write it from scratch. Thanks.
EDIT:
session controller
def create
user = User.authenticate(params[:email], params[:password])
if user && user.confirmed == true
cookies.permanent.signed[:remember_token]
redirect_to account_path(user.id), :notice => "Welcome, #{user.first_name}"
else
flash.now.alert = "Invalid email or password."
render "new"
end
end
Of course, after much trial and tribulation, I figured it out. Before, I was redirecting the routes to a new controller where they can edit their password instead of just sending them to the route that just confirms the code. Silly mistake that cost me a lot of headache, but live and learn. Thanks everyone who looked into it.
You might want to search for some tutorials to at least guide you through the process, you'll get a better feel for coding rails correctly.
Basically your problem is that your not doing a check to see if the user is confirmed or not on login. One way would be to add this inside your session create method.
if user && user.confirmed?
The best solution though is probably to use filters like this
before_filter :authenticate, :only => [:new, :create, :edit, :destroy]
Then you have an authenticate method that checks for a current user, and then have logic that says the current_user can only be a confirmed user. This will check that the user is valid on all the pages that they need to be, instead of only on login.

Resources