I am implementing omniauth for twitter and I have run into an error "Couldn't find User with 'id'=true" the error is pointing to the application controller current_user metho. Heere is my current_user method:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
helper_method :current_user
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
end
and here is my User model:
class User < ActiveRecord::Base
def self.find_or_create_by_auth(auth_data)
user = where(provider: auth_data[:provider], uid: auth_data[:uid]).first_or_create
user.update(name: auth_data[:info][:name])
end
end
and finally the SessionController is below:
class SessionsController < ApplicationController
def create
#user = User.find_or_create_by_auth(request.env["omniauth.auth"])
session[:user_id] = #user
redirect_to products_path, notice: "logged in as "
end
def destroy
session[:user_id] = nil
redirect_to root_path, notice: "Goodbye!!"
end
end
When I trying to log in the error pops and i can't get past login to load my index page.
I think your problem should be solved with following modification in User.find_or_create_by_auth method:
def self.find_or_create_by_auth(auth_data)
# all previous code
# you should return user from here
# your current code returned true of false
user
end
Also you should save #user.id in session, not full #user object:
session[:user_id] = #user.id
I finally solved this, the issue was that i had deleted the previous authenticated twitter user in the db and i was trying to authenticate with the same credentials again on the app.
so what i did is create a new twitter app and use different keys to authenticate into my rails App... hoep this explains it thanks
Related
I'm trying to set in cookies target_path that non authorized user tried to reach and after authorization redirect him to the target. Everything works fine and good, but then I tried to set as target edit_test_path or create_test_path and other methods with POST/PATCH/PUT requests and seems that no cookies are being set. What can be the case?
application.rb - I'm setting cookies here. authenticate_user! calling in almost every controller before_actions
class ApplicationController < ActionController::Base
protect_from_forgery with: :null_session
helper_method :current_user,
:logged_in?
private
def authenticate_user!
unless current_user
cookies[:target_path] = request.path_info
redirect_to login_path, alert: 'Verify Email or Password'
end
end
def current_user
#current_user ||= User.find_by(id: session[:user_id]) if session[:user_id]
end
def logged_in?
current_user.present?
end
end
sessions_controller.rb - I'm trying to redirect to the target from cookies here
class SessionsController < ApplicationController
def new; end
def create
user = User.find_by(email: params[:email])
if user&.authenticate(params[:password])
session[:user_id] = user.id
cookies[:target_path] ? (redirect_to cookies[:target_path]) : (redirect_to root_path) # With verb POST cookies don't work
else
flash.now[:alert] = 'Verify Email or Password'
render :new
end
end
def exit
session[:user_id] = nil
redirect_to login_path
end
end
I don't think, you can do this with POST/PUT/PATCH requests. When you are doing redirect_to, rails sends 302 Found reponse with location specified in parameter of redirect_to in your case cookies[:target_path] or root_path.
Browser then understends it should do redirect and sends GET request to URL specified in location header. You cannot nor should try to tell it to do POST/PUT/PATCH requests - these types of requests usually also require some kind of data (eg submitted form) that goes along with the request. You lost all of these data during redirect to login page anyway.
What I am trying to say - use these redirects only for GET requests. It will not work for POST/PUT/PATCH.
I'm trying to implement Wicked gem with Devise as I want users to go through different steps in order to complete their profiles. I'm a complete newbie so I would appreciate if you can give me a suggestion on what could be the problem.
The error I'm getting is this one and it shows when I try to continue from "Personal" to "Style" step. I guess it's a problem with saving the data:
NoMethodError in OnboardingController#update
undefined method `attributes' for nil:NilClass
**#user.attributes(user_params)**
These are my registration and onboarding controllers:
class RegistrationsController < Devise::RegistrationsController
protected
def after_sign_up_path_for(resource)
'/onboarding/personal'
end
def after_update_path_for(resource)
registration_steps_path
end
def new
super
end
def create
super
end
def update
super
end
def update_resource(resource, params)
if resource.encrypted_password.blank? # || params[:password].blank?
resource.email = params[:email] if params[:email]
if !params[:password].blank? && params[:password] == params[:password_confirmation]
logger.info "Updating password"
resource.password = params[:password]
resource.save
end
if resource.valid?
resource.update_without_password(params)
end
else
resource.update_with_password(params)
end
end
end
and
class OnboardingController < ApplicationController
include Wicked::Wizard
steps :personal, :stylefirst
def show
#user = current_user
render_wizard
end
def update
#user = current_user
#user.attributes(user_params)
render_wizard #user
end
end
With Devise, current_user is nil if no user is logged in. So your problem is that you're assigning #user = current_user on your update action without verifying a user is signed in.
If you want to ensure the update action is only available to signed in users then use the authenticate_user! helper action provided by Devise:
class OnboardingController < ApplicationController
before_filter :authenticate_user!, only: [:edit, :update]
# ...
end
The authenticate_user! helper method will redirect the user to the sign in page if they're not logged in. If the user successfully signs in, current_user will be set and they will be redirected back to the page the originally tried to access.
I'm learning Rails and I'm trying to restrict access to pages if a user hasn't logged in and to only allow them to view the login and sign up pages.
Currently, my code creates a session when a user logs in and clears it when the user logs out. I've got a Sessions helper so that I can check whether a user is logged in but I'm unsure how to redirect the user throughout the app if he/she's not logged in.
UPDATE:
As I posted the question, I managed to get something to work with a before_filter. Should I use a before_action or before_filter?
Do I need to copy the same method in all my controllers where I want to restrict access?
CODE:
/controllers/application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
include SessionsHelper
end
/controllers/sessions_controller.rb
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
log_in user
redirect_to user
else
flash.now[:danger] = 'Invalid email/password combination'
render 'new'
end
end
def destroy
log_out
redirect_to root_url
end
end
/helpers/sessions_helper.rb
module SessionsHelper
# Logs in the given user.
def log_in(user)
session[:user_id] = user.id
end
# Returns the current logged-in user (if any).
def current_user
#current_user ||= User.find_by(id: session[:user_id])
end
# Returns true if the user is logged in, false otherwise.
def logged_in?
!current_user.nil?
end
# Logs out the current user.
def log_out
session.delete(:user_id)
#current_user = nil
end
end
You can use a before_action. The rails guide has a nice section with an example on that:
class ApplicationController < ActionController::Base
before_action :require_login
private
def require_login
unless logged_in?
flash[:error] = "You must be logged in to access this section"
redirect_to new_login_url # halts request cycle
end
end
end
So I am using omniauth-facebook to create a log in using Facebook.
Here is my sessions_controller.rb
class SessionsController < ApplicationController
def create
user = User.from_omniauth(env["omniauth.auth"])
session[:user_id] = user.id
redirect_to "/sessions/menu"
end
def destroy
session[:user_id] = nil
redirect_to root_url
end
def new
end
def menu
puts user.name
end
end
Unfortunately I don't know how to access the user variable in the menu action. How would you guys recommend I do this?
Update
class SessionsController < ApplicationController
def create
#user = User.from_omniauth(env["omniauth.auth"])
session[:user_id] = #user.id
redirect_to "/sessions/menu"
end
def destroy
session[:user_id] = nil
redirect_to root_url
end
def new
end
def menu
puts #user
end
end
Even when I update it like so, it doesn't work
Unfortunately I don't know how to access the user variable in the menu
action. How would you guys recommend I do this?
Every time a request is made in your app for actions in SessionsController, a new instance of the SessionsController is created. So, instance variable set during create action would not be available when request for menu action is called as now you have a new instance of SessionsController which is why #user set in create action would not be available in menu. Also, if you use user (local variable) in this case, then its always local to the method/action in which its defined. So even that would not be available in menu.
By using facebook-omniauth gem you would receive Auth Hash in env["omniauth.auth"] which in turn you would use to create(new user) or initialize(in case of existing user) a user hopefully in from_omniauth method.
NOTE: env is request specific so env["omniauth.auth"] value will be present in create action but not in menu.
To resolve this, i.e., to access the created or initialized facebook user in menu action, you should make use of the user_id that you stored in session as below:
def menu
if session[:user_id]
#user = User.find(session[:user_id])
end
end
Also, if you would like to access the user in other actions as well then I would recommend to reuse the code by using before_action callback:
class SessionsController < ApplicationController
## Pass specific actions to "only" option
before_action :set_user, only: [:menu, :action1, :action2]
#...
def menu
puts #user.name
end
private
def set_user
if session[:user_id]
#user = User.find(session[:user_id])
end
end
end
where you can add specific actions via :only option
I am working on a basic authentication system for a rails app. The authentication is verifying account information from Active Directory using a net-ldap class (this part is working fine).
Something seems to be wrong with my session_helper however. Even though ActiveDirectoryUser.authenticate is successful, the signed_in helper always returns false. After signing in, the script redirects to root_path (default_controller's home) and then immediately redirects back to signin_path again-- as a result of the signed_in helper returning false.
See the code below. What am I missing?
Thanks
application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
include SessionsHelper
end
default_controller.rb
class DefaultController < ApplicationController
before_filter :signed_in_user
def home
end
private
def signed_in_user
redirect_to signin_path, notice: "Please sign in." unless signed_in?
end
end
sessions_helper.rb
module SessionsHelper
def sign_in(user)
#current_user = user
end
def current_user
#current_user ||= nil
end
def signed_in?
!#current_user.nil?
end
def sign_out
#current_user = nil
end
end
sessions_controller.rb
class SessionsController < ApplicationController
def new
end
def create
user = ActiveDirectoryUser.authenticate(params[:session][:username],params[:session][:password])
if user.nil?
# authentication failed
flash.now[:error] = 'Invalid email/password combination'
render 'new'
else
# authentication succeeded
sign_in #user
flash[:error] = 'Great success'
redirect_to root_path
end
end
def destroy
sign_out
redirect_to root_path
end
end
You should use session for to persist that kind of data (will be assessable for every request), it's user data. But I highly recommend you to use something like the devise gem that do all that authentication things and more for you. Why reinvent the weel right?
I believe this would work for you.
module SessionsHelper
def sign_in(user)
session[:user_id] = user.id
end
def current_user
ActiveDirectoryUser.find(session[:user_id]) ||= nil
end
def signed_in?
!session[:user_id].nil?
end
def sign_out
session[:user_id] = nil
end
end