I followed Ryan Bates'/RailsCasts tutorial for cookies login and remind me functionality in an application I am building.
[reference: http://railscasts.com/episodes/274-remember-me-reset-password?view=comments]
I wanted to introduce his OmniAuth functionality for the same application.
[reference: http://railscasts.com/episodes/235-omniauth-part-1]
I'm not using devise. I am correctly loading the Twitter app page, the error occurs when redirecting back to my application. I have the callback URL correctly set within Twitter.
I am getting an undefined method "authentications" for nil:NilClass error with OmniAuth and my authentications_controller. Specifically my console reads:
NoMethodError (undefined method authentications for nil:NilClass):app/controllers/authentications_controller.rb:9:in create'.
Here is the Authentications Controller code:
class AuthenticationsController < ApplicationController
def index
authentications = Authentication.all
end
def create
auth = request.env["omniauth.auth"]
current_user.authentications.create(:provider => auth['provider'], :uid => auth['uid'])
flash[:notice] = "Authentication was successful."
redirect_to authentications_url
end
Here is the current_user helper method in my Applications Controller.
private
def current_user
#current_user ||= User.find_by_auth_token!(cookies[:auth_token]) if cookies[:auth_token]
end
helper_method :current_user
User has many authentications.
Authentications belong to user.
I'd like to enable OmniAuth to work with Twitter so users can login with their Twitter account by avoiding the error I am receiving while maintaining my current_user code
Help is appreciated.
You're not taking into account the case where the user is not logged in.
What you're doing in your create action is associating the user's account on your site to his account on Twitter.
You can't use current_user without making sure that the user is logged in. Otherwise it will return nil, and that's why it's saying undefined method authentications for nil:NilClass.
Take a look at the next railscast that goes into more details: http://railscasts.com/episodes/236-omniauth-part-2
Related
I have rails app with:
Admin table with Devise authentication
User table with email and name without authentication (but session to remember them)
User can browse anywhere but now on certain pages I would like to enhance it and add authentication - allow user to create password and only with password it will be accessible but I am quite lost what is the best way to do it with the current setting?
I allow users to add their details like name and email and I am creating a cookie to remember them without any authentication or password:
UsersController
def create
user = User.find_or_create_by(email: params[:user][:email])
cookies.permanent.signed[:user_id] = user.id
session[:user_id] = user.id # for users/edit temporary
render json: user
end
Let's say I have this following method in User:
before_filter :authenticate_user!, only: :your_order
def your_order
end
If User will visit this page and didn't set up password before, how can I prompt him to create one and how can I require for him to login after with Devise? I am thinking of more solutions but none of them are perfect.
As per the specifications given the below mentioned criteria might help you.
def your_order #before_filter
if user.password.present?
# authenticate using valid_password? method of devise
else
#redirect user to say set_password
end
end
def set_password
#set the user password in this method and after successful completion redirect to login page where before filter your_order will be called
end
I'm working on google authentication for a rails app. Currently using the omniauth-google-oauth2 gem to implement Google auth. I've managed to have users sign in using google. However, I'd also like users to be able to sign up using google. My problem is that I've matched the google callback URL to a particular controller action (sessions#create).
Is it possible to choose between 2 redirect URIs based on whether users are signing in or signing up? Currently, my only idea is to create new google client credentials to be used for sign up, I hope there is a better way.
You don't need to have 2 redirect uris, you just need to do some more work when receiving the callback. For instance:
class SessionsController < ApplicationController
...
def create
email = auth_hash['info']['email'] # assuming your omniauth hash is auth_hash and you're requiring the email scope
#user = User.find_by(email: email) if !email.blank? # assuming your user model is User
if #user
login_user(#user) # use your login method
elsif !email.blank?
#user = User.new(name: auth_hash['info']['name'], email: email)
unless #user.save!(validate: false) # validate false because I'm enforcing passwords on devise - hence I need to allow passwordless register here)
# deal with error on saving
end
else
# deal with no found user and no email
end
end
protected
def auth_hash
request.env['omniauth.auth']
end
end
I've written all steps but the creation process can be shortened to:
#user = User.create_with(name: auth_hash['info']['name']).find_or_initialize_by(email: email)
#user.save! if #user.new_record?
if #user
login_user(#user)
else
# deal with no user
end
Nonetheless, you can't be sure the user is going to give you scope access to the email, so personally I think the first version, even if a bit lengthier is more robust. Then on the shorter version there's also the problem of, if #user is false, why is so? And will require you to add more logic to figure out why is that, whereas in the first one it's much easier to apply the correct response to each situation.
I'm making an application that uses Facebook info with Rails, Omniauth and Koala.
Using Ryan Bates Facebook Railscasts, I use this facebook method on my User model, to get the users uid, auth_token, etc from Facebook's API.
def facebook
#facebook ||= Koala::Facebook::API.new(oauth_token)
end
I also have another method that calls the facebook method to get info from the user's friends,
lets call this, facebook_friends
On my UsersController show action I have this:
def show
#user = User.find(params[:id])
#friends = #user.facebook_friends
end
So I use those two instance variables in my show template.
Writing tests with RSpec
Here's one test I'm trying to make
feature 'As a logged out user' do
background do
#user = FactoryGirl.create(:user)
end
scenario 'redirects to root path' do
visit user_path #user
page.current_path.should == root_path
end
end
But when I run it, I get this error:
1) As a logged out user redirects to root path
Failure/Error: visit user_path #user
Koala::Facebook::APIError:
OAuthException: Invalid OAuth access token.
# ./app/models/user.rb:45:in `friends_birthday'
# ./app/controllers/users_controller.rb:7:in `show'
# ./spec/requests/user_integration_spec.rb:22:in `block (2 levels) in <top (required)>'
Since I dont want to use a valid access token everytime I run the tests
How can I fake the facebook call on the User model using RSpec?
VCR is the answer, you could check excelled railscasts about it http://railscasts.com/episodes/291-testing-with-vcr
Some examples you can find in my sample project: https://github.com/lucassus/locomotive/blob/master/spec/features/user_facebook_connect_spec.rb
I'm using Janrain to handle user sessions in my Ruby on Rails app. It appears to be working, however, I don't know how to tell if a user is logged in or not or access the current user's information. After the user signs in, is there a session variable created?
Assuming you are referring to Janrain Social Login(Engage), once the user authenticates through a Social Provider the widget gets a Janrain OAuth token that is valid for 60 minutes. You can use that token to retrieve the user's profile data through this API end point: (https://{your-engage-domain.com}/api/v2/auth_info).
Janrain Social Login does not maintain any log in state related session data. It simply facilitates authentication and normalizes the retrieval of user profile data from multiple authentication providers. Once a successful authentication event happens it is up to your server to validate the authentication token and then establish any form of authorization session related work.
Most Social Providers return access tokens that are valid for 30-60 days.
try 'current_user' variable, it works in most of the rails authentication libs, e.g.:
#in the erb file:
<% current_user = session[:user_id] %>
# or in the rb file:
class MusicController < ApplicationController
before_filter :authenticate_user! # just like devise
def index
# same methods and api as devise.
return if signed_in? and current_user.email
end
end
# put this method in application_controller.rb
def current_user
#current_user ||= User.find_by_id(session[:user_id])
end
more details refer to this example: https://github.com/hatem/janrain-engage-demo
I am following the example write in Chapter 14 "Logging In" of the book.
I have my view in "127.0.0.1:3000/login" working well, but if i insert my user and password it returns this error:
NoMethodError in SessionsController#create
undefined method `authenticate' for #< User:0x9f75978>
How to solve it?
create method sessions_controller.rb is:
def create
user = User.find_by_name(params[:name])
if user and user.authenticate(params[:password])
session[:user_id] = user.id
redirect_to admin_url
else
redirect_to login_url, alert: "Invalid user/password combination"
end
end
It's trying to call authenticate on User, this means you probably don't have an authenticate method on user. If you do, make sure it's not private.
The code you give tries to call the authenticate method on an instance of a User object.
Based on you comment re: User.authenticate(name, password) you have an authenticate method only on the User class - and it takes both name and password as parameters.
To call the User class method, you'd instead use the following:
user = User.find_by_name(params[:name])
if user && User.authenticate(params[:name], params[:password])
Alternatively, look for an instance-level method called authenticate which (as #caulfield mentioned above) would look something like:
def authenticate(password)
# does stuff
end
instead of User.authenticate(name,password)