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
Related
right now I have a problem with login. I want the user to go the previous page where he/she pressed the login-button. In my code right now I just have redirect_to back which just sends the user back one level but I want it to be two levels. How do I do that?
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
sign_in user
redirect_to back
else
flash.now[:error] = 'Invalid email/password combination'
render 'new'
end
end
def destroy
sign_out
redirect_to root_url
end
end
Thanks!
one of the solution maybe store the url inside the cookies, and redirect to that url after sign in
cookies[:return_to] = {
value: request.fullpath,
expires: 1.hour.from_now
}
redirect_to sign_in_path
after login, just call
path = cookies[:return_to]
cookies.delete(:return_to)
redirect_to path
The simplest solution is to probably follow the outline that the devise gem provides in their how-to. TL;DR - store the path in the session and use that as your redirect after sign in.
I've watched this video and I made the authentication correctly.
class SessionsController < ApplicationController
def create
user = User.from_omniauth(env["omniauth.auth"])
session[:user_id] = user.id
redirect_to root_url
end
def destroy
session[:user_id] = nil
redirect_to root_url
end
end
How can I redirect a user to the location he was on previously? Not root_url. What should be in SessionsHelper? I've also looked at Hartl's tutorial sample code, but I'm using FB API.
You may redirect_to :back in your controller or you can use devise gem for authentication with gem 'omniauth-facebook'. It work as you wish out of the box. See wiki page at devise git for implementation
So on my local host, the sign in persists and the user is signed in. However, on Heroku, after signing in, it doesn't even recognize that the user is still signed in.
In the heroku logs, it starts
-A get request for log in
-SessionsController renders a new session
-A user is committed
-A POST request to start sessions occurs
-redirect occurs
-user sign in apparently isn't saved
-This seems like an issue with the cookie?
This is my Sessions Controller
class SessionsController < ApplicationController
def new
end
def create
user = User.authenticate(params[:email], params[:password])
if user
session[:user_id] = user.id
sign_in user
redirect_to root_url, :notice => "Logged in!"
else
flash.now.alert = "Invalid email or password"
render "new"
end
end
def destroy
sign_out
redirect_to posts_url, :notice => "Logged out!"
end
end
This is my SessionsHelper
module SessionsHelper
def sign_in(user)
cookies.permanent[:remember_token] = user.remember_token
self.current_user = user
end
def signed_in?
!current_user.nil?
end
def current_user=(user)
#current_user = user
end
def current_user
#current_user ||= User.find_by_remember_token(cookies[:remember_token])
end
def current_user?(user)
user == current_user
end
def is_admin?
signed_in? ? current_user.admin : false
end
def sign_out
current_user.update_attribute(:remember_token,
User.encrypt(User.new_remember_token))
cookies.delete(:remember_token)
self.current_user = nil
end
end
In my user model I do save a remember_token before saving
before_save :create_remember_token
def create_remember_token self.remember_token = User.encrypt(User.new_remember_token)
Any ideas would be much appreciated!
Following your code step by step:
A User is created with no remember token.
The sign_in method is being called. Nil is saved to cookies as User has no remember token yet.
Your current_user method sets #current_user to nil because User.find_by(nil) returns nil, and doesn't raise an exception as User.find(nil) would.
While I'm not 100% sure this is the reason why your code is breaking (as I can't see what callbacks you've written), it certainly makes sense. If in your local environment you create a new user from scratch I assume that it'll break as well. The only place I see you defining a remember token for the user is in the sign_out method - if you were signed in while implementing the feature, signed out to test it, and signed back in, the code would seemingly work.
This issue can be fixed by using a callback to set the remember_token of the User on create via callback, or in the sign_in method.
As a side note, if you're following Hartl's implementation of sessions, I'd definitely revisit that as you made a fairly large error in how you handle the remember tokens. The encrypted token should be stored on your database, and the unencrypted one should be stored on the cookie. Then when using find_by, you should be encrypting that cookie to find the user in the database. As it currently stands you're saving the naked remember token in both the db and cookie, which poses security issues.
Im following the tutorial on http://ruby.railstutorial.org
I've manage to get the part when user will get prompted to login when accessing a restricted page then be redirected back to the restricted page after logging in.
However, after the login, I've logged out and logged in with another user, i will get redirected back to the page, and not the default main page for a fresh session.
My session Controller
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by_email(params[:session][:email])
if user && user.authenticate(params[:session][:password])
sign_in user
redirect_back_or user
# Sign the user in and redirect to the user's show page.
else
# Create an error message and re-render the signin form.
flash.now[:error] = 'Invalid email/password combination'
render 'new'
end
end
....
end
My session helper
module SessionsHelper
....
def redirect_back_or(default)
redirect_to(session[:return_to] || default)
session.delete(:return_to)
end
def store_location
session[:return_to] = request.fullpath
end
end
Add session.delete(:return_to) to create method
def create
user = User.find_by_email(params[:session][:email])
if user && user.authenticate(params[:session][:password])
sign_in user
session.delete(:return_to)
redirect_back_or user
# Sign the user in and redirect to the user's show page.
else
# Create an error message and re-render the signin form.
flash.now[:error] = 'Invalid email/password combination'
render 'new'
end
end
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