Omniauth NoMethodError in SessionsController - omniauth

Hi I'm trying to run Railscast's #241 - using Omniauth for twitter - eventually with facebook.The app I'm trying to make will require Facebook's login, but since there is a video on how to use twitter to login, might as well follow it first but I'm bumping into problems.
I keep getting this error after I signed into Twitter. "NoMethodError in SessionController#create" - undefined method `[]' for nil:NilClass. Here is the related code.
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
end
I have also set the website and callback link via dev.twitter.com as follows:
website: "http://127.0.0.1:3000"
Callback: "http://127.0.0.1:3000/auth/twitter/callback"
Omniauth.rb in config/initializers:
Rails.application.config.middleware.use OmniAuth::Builder do
provider :twitter, 'CONSUMER_KEY', 'CONSUMER_SECRET'
end
(I've already provided the consumer key and secret)
Application.html.erb
<div id="user_nav">
<%= link_to "Sign in with Twitter", "/auth/twitter" %>
</div>
Added this line in routes.rb
match "/auth/:provider/callback" => "sessions#create"
User.rb
class User < ActiveRecord::Base
attr_accessible :name, :provider, :uid
def self.create_with_omniauth(auth)
create! do |user|
user.provider = auth["provider"]
user.uid = auth["uid"]
user.name = auth["user_info"]["name"]
end
end
I don't know where I went wrong

Ok I found the problem. Took me a while.
Twitter changed their "user_info" element into "info"
So instead of
user.name = auth["user_info"]["name"]
it is
user.name = auth["info"]["name"]
Next thing to change was in sessions controller from
session[:user_id] = user.id
to
session[:user_id] = user.uid

Related

Clearance failure when forbidden password reset

I’m using clearance and love it, but I'm having trouble resetting passwords. I type in my email to reset the password, which works, but then when I try to navigate to the edit password page using the reset token, I get the failure when forbidden flash error “Please double check the URL or try submitting the form again” and it redirects me back. I get the same error in my tests.
I think this has something to do with my before_action statements, but I just don’t know how to fix them. I have researched questions like this to no avail.
I'm sure it's a stupid question, but I'm new so I really appreciate any help. Please let me know if this isn't enough code.
class UsersController < Clearance::UsersController
before_action :require_login, only: [:create] # does this need to be in both user controllers?
...
def user_params
params.require(:user)
end
end
And here is the clearance controller.
class Clearance::UsersController < ApplicationController
before_action :require_login, only: [:create]
require 'will_paginate/array'
def new
#user = user_from_params
render template: 'users/new'
end
def create
#user = user_from_params
#user.regenerate_password
if #user.save
sign_in #user unless current_user
UserMailer.welcome_email(#user).deliver!
redirect_to users_path
else
render template: 'users/new'
end
end
def edit
#user = User.friendly.find(params[:id])
end
def update
#user = User.friendly.find(params[:id])
if #user.update(permit_params)
redirect_to #user
flash[:success] = "This profile has been updated."
else
render 'edit'
end
end
private
def avoid_sign_in
redirect_to Clearance.configuration.redirect_url
end
def url_after_create(user)
dashboards_path(user)
end
def user_from_params
user_params = params[:user] || Hash.new
is_public = check_public_params(user_params)
first_name = user_params.delete(:first_name)
last_name = user_params.delete(:last_name)
email = user_params.delete(:email)
password = user_params.delete(:password)
parish = user_params.delete(:parish)
division = user_params.delete(:division)
admin = user_params.delete(:admin)
Clearance.configuration.user_model.new(user_params).tap do |user|
user.first_name = first_name
user.last_name = last_name
user.password = password
user.email = email
user.is_public = is_public
user.parish_id = parish.to_i
user.division = division
user.admin = admin
end
end
def permit_params
params.require(:user).permit(:first_name, :last_name, :email, :password, :is_public, :parish_id, :division, :admin)
end
end
EDIT: relevant portions of routes.rb
Rails.application.routes.draw do
resources :passwords, controller: "clearance/passwords", only: [:create, :new]
resource :session, controller: "clearance/sessions", only: [:create]
resources :users, controller: "clearance/users", only: [:create] do
resource :password,
controller: "clearance/passwords",
only: [:create, :edit, :update]
end
get "/sign_in" => "clearance/sessions#new", as: "sign_in"
delete "/sign_out" => "clearance/sessions#destroy", as: "sign_out"
get "/sign_up" => "clearance/users#new", as: "sign_up"
constraints Clearance::Constraints::SignedOut.new do
root to: 'high_voltage/pages#show', id: 'landing'
end
constraints Clearance::Constraints::SignedIn.new do
# root to: 'dashboards#index', as: :signed_in_root
root to: 'high_voltage/pages#show', id: 'parish_dashboard', as: :signed_in_root
end
# constraints Clearance::Constraints::SignedIn.new { |user| user.admin? } do
# root to: 'teams#index', as: :admin_root
# end
resources :users do
collection { post :import }
end
It turns out there was a conflict between the way I was finding the user instance in the password reset link. Clearance finds users simply by using #user, but since I'm using FriendlyId I needed to change that to #user.id.
So instead of...
<%= link_to 'Change My Password', edit_user_password_url(#user, token: #user.confirmation_token.html_safe) %>
I did
<%= link_to 'Change My Password', edit_user_password_url(#user.id, token: #user.confirmation_token.html_safe) %>
Thanks, Thoughbot, for this great gem!

Using flash to notify user of previous registration

I'm trying to use my Users controller to notify the user when their email has already been used in a registration, but even when the email already exists, I still get the error "Plase validate your input and try again," rather than "You've already registered! Thanks for being enthusiastic!" Is using the controller not the create way of achieving this behavior?
In the rails console (assuming "foo#bar.com" is in the database"), when I use user = User.new(name:"Example", email:"foo#bar.com") then User.find_by_email(user.email) it does return the proper User entry, so I'm not sure if I'm on the right track and just executing it incorrectly or what. Any ideas?
users_controller.rb:
class UsersController < ApplicationController
def new
#user = User.new(params[:user])
end
def create
#user = User.new(params[:user])
if #user.save
flash[:success] = "Thanks for supporting cofind! We'll be in touch!"
redirect_to root_path
UserMailer.welcome_email(#user).deliver
else
if #user.email == User.find_by_email(#user.email)
flash[:error] = "You've already registered! Thanks for being enthusiastic!"
redirect_to root_path
else
flash[:error] = "Plase validate your input and try again."
redirect_to signup_path
end
end
end
end
user.rb:
class User < ActiveRecord::Base
attr_accessible :email, :name
before_save { |user| user.email = email.downcase }
validates :name, presence: true
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
end
this line
if #user.email == User.find_by_email(#user.email)
checks the user's email (a string) against a user record (an ActiveRecord object) which will always be false. You should change that to
if User.where(email: #user.email).exists?

Why doesn't my current_user helper method work during tests?

I have a Rails 3.2.3 application and I am using MiniTest and Capybara to run my integration tests. I rolled my own authentication and created a current_user helper_method in my application_controller.rb.
My application layout file only displays certain links, like logout, etc., when a user is logged in.
But the current_user method does not work during tests. It looks like this:
class ApplicationController < ActionController::Base
protect_from_forgery
private
def authenticate
if current_user.blank?
redirect_to root_url, :alert => "You must first log in."
end
end
def authenticate_admin
unless current_user and current_user.admin?
redirect_to root_url, :alert => "You must be logged in as an administrator to access this feature."
end
end
def current_user
begin
#current_user ||= User.find(session[:user_id]) if session[:user_id]
rescue
nil
end
end
helper_method :current_user
end
So in my application.html.erb file there is:
<% unless current_user.blank? %>
<li><%= link_to logout_path %></li>
So this works when I test it through the browser, but not in my test. "current_user" ends up being nil.
I am new to BDD, but is there something that prevents sessions from being created during tests? What am I missing?
NOTE: helper methods defined in controllers are not included.
from here.
The solution is to organize them in a dedicated helper class.

devise+omniauth devise helper like current_user,user_signed_in? not working

I am using devise and create login with Facebook using omniauth, but having problem of lost the devise helper methods access like current_user and user_signed_in? methods are not working.
EDIT
AuthenticationController
def create
omniauth = request.env["omniauth.auth"]
user = User.find_by_provider_and_uid(omniauth["provider"], omniauth["uid"]) || User.create_with_omniauth(omniauth)
session[:user_id] = user.id
redirect_to dashboard_path(user.id), :notice => "Signed in!"
end
redirect_to USercontroller dashboard method
UserController
before_filter :logged_in
def dashboard
#user = User.find(params[:id])
#comment = Comment.new
#comments = #user.comments.all.paginate(:page => params[:page], :per_page => 5)
end
so here control should go to dashboard method after checking logged_in method in ApplicationController
logged_in method in ApplicationController
Application Controller
def logged_in
if user_signed_in?
return true
else
redirect_to root_path
flash[:message] = "please login"
end
end
when I logged in using facebook following code generated at console
Started GET "/users/52/dashboard" for 127.0.0.1 at Thu Mar 29 12:51:55 +0530 2012
Processing by UsersController#dashboard as HTML
Parameters: {"id"=>"52"}
Redirected to http://localhost:3000/
Filter chain halted as :logged_in rendered or redirected
Completed 302 Found in 2ms (ActiveRecord: 0.0ms)
in the above code control is render from logged_in method to root_path but it shold render dashboard_path
So I am guessing User_signed_in? helper is not working I also use current_user in stead of that generate same error
As I see, user_signed_in? is working, but returns false, as for Devise user is not logged in. To fix this, just replace the session id storing with Devise sign_in method in your controller action:
def create
omniauth = request.env["omniauth.auth"]
user = User.find_by_provider_and_uid(omniauth["provider"], omniauth["uid"]) || User.create_with_omniauth(omniauth)
sign_in(:user, user)
# actually if you really really need that id in the session, you can leave this line too :)
session[:user_id] = user.id
redirect_to dashboard_path(user.id), :notice => "Signed in!"
end
After creating the user account via Facebook, how do you sign in the user?
You should still be using devise helpers like sign_in_and_redirect. Something like:
user = User.build_from_omniauth(omniauth)
if user.save
sign_in_and_redirect(:user, user)
end
Then you should be able to use helpers like current_user and user_signed_in? (which just check if current_user is not nil).
Taking a look at your edit, my answer is still valid. What you need to do is use sign_in_and_redirect(:user, user) instead of just setting the id in the session.
You can easily customize where the user is redirected after sign in with devise.
Another thing, remove this logged_in filter, Devise has a authenticate_user! method that you can use as a before_filter. It will redirect the user to the sign in page, and when they login, it will redirect them to the page they were trying to access.
You're using Devise, so try to take advantage of that, and go read the doc ;)

Rails 3 render partial from another controller (error: ActionView::MissingTemplate)

I'm trying to include a login (username / password) in the header of my application.html.erb. I am getting this error:
Missing partial /login with {:handlers=>[:rjs, :builder, :rhtml, :erb, :rxml], :locale=>[:en, :en], :formats=>[:html]} in view paths "/app/views"
This is happening when I make this call in my application.html.erb:
<%= render '/login' %>
'/login' is defined in my routes.rb as:
match '/login' => "sessions#new", :as => "login"
UPDATE: here is my sessions controller:
class SessionsController < ApplicationController
def create
if user = User.authenticate(params[:email], params[:password])
session[:user_id] = user.id
user.last_login = Time.now
user.save
redirect_to root_path, :notice => "login successful"
else
flash.now[:alert] = "invalid login / password combination " # don't show pass + params[:password]
#render :action => "new"
redirect_to login_path, :notice => "wrong user pass"
end
end
def destroy
reset_session
redirect_to root_path, :notice => "successfully logged out"
end
end
I have seen in other posts that this can be due to not defining a variable in a controller action, but since this is a session, and it is in the application.html.erb (application_controller.rb), I'm not sure how to do this. Anybody know how to do this? Thanks!
<%= render "sessions/login", :#user => User.new %>
will render login partial of sessions view, i.e. '_login.html.erb' in views/sessions and instantiate #user to new user so that it can be referenced directly in the partial as :
form_for #user, :url => sessions_path do |f|
f.text_field :email
Check your file extension in my case file extension was rhtml, I changed it into html.erb.
Now its working fine.
Note:
This file with rhtml extension was working fine in rails <= 3.0.10. But stopped working in rails 3.1.12. So I changed its extension as mentioned above.