Devise not binding session (Omniauth) in Rails 4 - ruby-on-rails

I use Omniauth with Devise in my rails app. I managed to get the user signed in correctly when they are using omniauth (in this case, facebook). When the user try to login by using their email and password instead of omniauth, the app will still logged the user in but it does not store the session. So, there is no sign out button being shown and the user cannot do the thing that he/she suppose to do.
This is my route for user :
devise_for :users, path_names: {sign_in: "login", sign_out: "logout"}, controllers: {registrations: 'registrations', omniauth_callbacks: "omniauth_callbacks"}, :skip => [:sessions]
as :user do
get 'sign-in' => 'devise/sessions#new', :as => :new_user_session
post 'sign-in' => 'devise/sessions#create', :as => :user_session
get '/users/sign_out' => 'devise/sessions#destroy'
resources :users_admin, :controller => 'users'
end
This is my OmniauthCallbacksController:
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def all
user = User.from_omniauth(request.env["omniauth.auth"])
if user.persisted?
session[:user_id] = user.id
sign_in_and_redirect user, notice: "Signed in!"
else
session["devise.user_attributes"] = user.attributes
redirect_to new_user_registration_url
end
end
alias_method :facebook, :all
end
This is my SessionController:
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
And the tutorial that I am following is here: https://www.youtube.com/watch?v=X6tKAUOMzCs
Please help..thanks!

The code of SessionsController is wrong. In create action you're trying to authenticate user from omniauth, but this controller is not used for OAuth authentication. This is why it doesn't save anything in session. Moreover, there's seem to be a typo: you use env["omniauth.auth"] instead of request.env["omniauth.auth"].
Actually, you don't need to modify SessionsController or create your own. Devise's default SessionsController works fine. You just need to turn on Omniauthable, connect omniauth-provider and that's all. Route settings would look like this:
devise_for :users, controllers: {omniauth_callbacks: 'omniauth_callbacks'}
You can find the code from the video in this repo: https://github.com/railscasts/235-devise-and-omniauth-revised/tree/master/blog-after Hopefully, it will help you.

For me, only add sign_in user after persiste solves. The sign_in user will add the user into the current_user and set the session.
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def all
user = User.from_omniauth(request.env["omniauth.auth"])
if user.persisted?
sign_in user
else
session["devise.user_attributes"] = user.attributes
redirect_to new_user_registration_url
end
end
end
end

Related

sign_in_and_redirect after facebook auth is recalling itself

I have omniauth-facebook to sign up/sign in. After signin, I want to redirect the user to the page where they hit the auth block. I'm using sign_in_and_redirect but it seems to be calling the last url, which in this case is
http://localhost:3000/auth/facebook?callback_url=localhost%2Fauth%2Ffacebook%2Fcallback
So it keeps doing:
Redirected to http://localhost:3000/auth/facebook?callback_url=localhost%2Fauth%2Ffacebook%2Fcallback
And it keeps calling this callback in a loop until it crashes. Here is the omniauth controller:
def facebook
generic_callback( 'facebook' )
end
def generic_callback( provider )
#identity = Identity.find_for_oauth env["omniauth.auth"]
if #identity.user != nil
#user = #identity.user || current_user
else
#user = User.from_omniauth(request.env["omniauth.auth"])
end
if #user.nil?
#user = User.create( email: #identity.email || "" )
#identity.update_attribute( :user_id, #user.id )
end
if #user.email.blank? && #identity.email
#user.update_attribute( :email, #identity.email)
end
if #user.persisted?
#identity.update_attribute( :user_id, #user.id )
#user = User.find(#user.id)
sign_in_and_redirect #user, event: :authentication
return
else
session["devise.#{provider}_data"] = env["omniauth.auth"]
redirect_to new_user_registration_url
return
end
end
The devise helper:
def sign_in_and_redirect(resource_or_scope, *args)
options = args.extract_options!
scope = Devise::Mapping.find_scope!(resource_or_scope)
resource = args.last || resource_or_scope
sign_in(scope, resource, options)
redirect_to after_sign_in_path_for(resource)
end
Routes
resources :users
devise_for :users, controllers: { registrations: "users/registrations", sessions: "users/sessions", :omniauth_callbacks => "users/omniauth_callbacks" }, path: '', path_names: { sign_in: 'sign_in', sign_out: 'logout', sign_up: 'sign_up'}
match '/auth/:provider/callback', to: 'sessions#create', via: [:get, :post]
How can I make it sign in regularly?
Ok, here's the problem. This line creates the recursion:
match '/auth/:provider/callback', to: 'sessions#create', via: [:get, :post]
Change it to:
get 'auth/facebook/callback', to: 'users/omniauth_callbacks#facebook'
If you'll refactor #generic_callback method a bit to use params[:provider] instead of the provider argument, you'll then be able to get rid of hard-coding of the omniauth providers in your routes:
get 'auth/:provider/callback', to: 'users/omniauth_callbacks#generic_callback'
To clarify the problem:
In the implementation described in the question users/omniauth_callbacks#generic_callback is not called at all. Instead you're trying to call sessions#create which is used to sign in users with the app credentials (usually, email and password), whereas in the omniauth case user should be created and authenticated in a specific way (via generic_callback and sign_in_and_redirect in your case).
Simply put:
You don't have to call sessions#create to create user session here, #generic_callback with #sign_in_and_redirect does just that.

Redirect User to Signup

Using before_action :authenticate_user! to check if user logged in. But it sends users to login instead of signup.
Tried different ways of directing user to signup instead of login, but they do not send the user back to the original page after successful signup.
How can I send a user to signup and direct a user back to the original page afterwards?
Attempts:
before_filter :auth_user
def auth_user
redirect_to new_user_registration_url unless user_signed_in?
end
Routes File
Rails.application.routes.draw do
devise_for :installs
resources :orders
resources :products
devise_for :users
get 'dashboard' => 'pages#dashboard'
get 'contact' => 'pages#contact'
get 'cart' => 'carts#index'
root 'pages#home'
What you are looking for is a referer.
Having your before_filter working, you can override the Devise's after_sign_up_path_for:
def after_sign_up_path_for(user)
request.referer
end
EDIT
Since request.referer is somehow not working, I think you could go with a session-based solution:
def auth_user
session[:before_sign_up_path] = request.fullpath
redirect_to new_user_registration_url unless user_signed_in?
end
def after_sign_up_path_for(user)
session[:before_sign_up_path]
end
Devise uses warden for authentication. Thus to override the whole behavior you have to override the way devise asks it to handle authentication failures.
Create a class that extends Devise::FailureApp
class RedirectToSignUp < Devise::FailureApp
def redirect_url
new_user_registration_url
end
def respond
if http_auth?
http_auth
else
redirect_to new_user_registration_url
end
end
end
Add this to your devise initializer config/initializers/devise.rb
config.warden do |manager|
manager.failure_app = RedirectToSignUp
end
I believe this will solve your problem and will redirect you to the page you were at since you are overriding only the redirect route.
I haven't tried it but this seems to be what you are looking for. Note that it says that it is out of date. So you should see the source code if you want to understand what's going on.
Navigating through the source code of Devise(this and this) my guess is that you can extend Devise::RegistrationsController and then override the method after_sign_up_path_for to be stored_location_for(:user) || root_path.
the way I like to set up my users area using devise is:
# config/routes.rb
devise_for :users, :controllers => {
registrations: 'users/registrations',
sessions: "users/sessions",
passwords: 'users/passwords',
confirmations: 'users/confirmations'
}
authenticate :provider do
namespace :providers do
....
end
end
Then I have a controller that manages all other users controllers like this
#app/controllers/user_controller.rb
class UserController < ApplicationController
before_filter :authenticate_user!
layout 'users/default'
before_filter :check_user_active
private
def check_user_active
unless current_user.active
flash[:notice]= t(:user_not_active)
sign_out current_user
redirect_to new_user_session_path
end
end
end
all my other user controllers look inherit from it like
#app/controllers/users/users_controller.rb
class Users::UsersController < UserController
...
end
I hope that this helps.
This is very simple
User store_location_for method
Like
before_filter :auth_user
def auth_user
unless user_signed_in?
store_location_for(:user, request.fullpath)
redirect_to new_user_registration_url
end
end

Google+ Sign Up with Devise and Rails (google_oauth2)

I'm getting the following error when trying to sign in through google+ using the google_oauth2 gem.
undefined method `find_for_google_oauth2' for #<Class:0x007ff70a337148>
Here's the three files I've altered for sign up.
user.rb
def google_oauth2
user = User.from_omniauth(request.env["omniauth.auth"])
if user.persisted?
flash.notice = "Signed in Through Google!"
sign_in_and_redirect user
else
session["devise.user_attributes"] = user.attributes
flash.notice = "You are almost Done! Please provide a password to finish setting up your account"
redirect_to new_user_registration_url
end
end
omniauth_callbacks_controller.rb
def google_oauth2
# You need to implement the method below in your model (e.g. app/models/user.rb)
#user = User.find_for_google_oauth2(request.env["omniauth.auth"], current_user)
if #user.persisted?
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Google"
sign_in_and_redirect #user, :event => :authentication
else
session["devise.google_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
and I've added config.omniauth :google_oauth2 in my devise.rb file.
routes.rb
devise_for :users, :controllers => { :registrations => "registrations", :sessions => "sessions", :omniauth_callbacks => "users/omniauth_callbacks" }
You are calling find_for_google_oauth2 from the omniauth_callbacks_controller, but you are using the wrong method name google_oauth2. You should replace google_oauth2 with find_for_google_oauth2.
And it seems like the code in user.rb is incorrect because it contains the controller code. Do you see it looks exactly the same like your controller code? :)
Correct code for user.rb
def self.find_for_google_oauth2(access_token, signed_in_resource=nil)
data = access_token.info
user = User.where(:email => data["email"]).first
# Uncomment the section below if you want users to be created if they don't exist
# unless user
# user = User.create(name: data["name"],
# email: data["email"],
# password: Devise.friendly_token[0,20]
# )
# end
user
end
Read more here: https://github.com/zquestz/omniauth-google-oauth2#devise

Devise's after_sign_in_path_for works after signing in normally, but does not work after signing in through Facebook or Twitter. Why is that?

I am working on a rails app right now that allows user's to log in normally, via twitter, and via Facebook as well. I am using devise, omniauth-twitter, and omniauth-facebook.
After signing up/registering, I want to redirect users to the page verify_user_email_path. I have my custom devise RegistrationsController which overrides the standard devise RegistrationsController. It looks like this:
class CustomDeviseControllers::RegistrationsController < Devise::RegistrationsController
def verify_email
flash[:notice] = ""
end
def update_email
#user = User.find(current_user.id)
params[:user].delete(:current_password)
if #user.update_without_password(devise_parameter_sanitizer.sanitize(:account_update))
flash[:notice] = "Welcome! You have signed up successfully."
sign_in #user, :bypass => true
redirect_to bookshelf_index_url
else
#error_message = "You have entered an invalid email address"
render "verify_email"
end
end
protected
def after_sign_up_path_for(resource)
# I WANT TO REDIRECT USERS HERE AFTER SIGNING UP
verify_user_email_path
end
end
As you can see, after any user signs up, they should be redirected to verify_user_email_path. However/sadly, users are only redirected if they sign up normally. So when users register on my site (they enter in an email, a password, and then confirm their password), after_sign_up_path_for correctly redirects users to verify_user_email_path. If users sign up via Facebook/Twitter, the users are simply redirected to the root_url instead. That is the bug I need to fix.
This is my custom OmniauthCallbacksController (underneath the all caps comment is where I sign users in):
class CustomDeviseControllers::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def self.provides_callback_for(provider)
class_eval %Q{
def #{provider}
#user = User.from_omniauth(request.env["omniauth.auth"])
if #user.persisted?
# HERE IS WHERE I SIGN USERS IN
sign_in_and_redirect #user, event: :authentication
set_flash_message(:notice, :success, kind: "#{provider}".capitalize) if is_navigational_format?
else
session["devise.#{provider}_data"] = env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
}
end
[:twitter, :facebook].each do |provider|
provides_callback_for provider
end
end
Aaaand here, is my config/routes.rb:
Rails.application.routes.draw do
devise_for :users, :controllers => {
omniauth_callbacks: 'custom_devise_controllers/omniauth_callbacks',
registrations: 'custom_devise_controllers/registrations' }
devise_scope :user do
get "users/verify_email" => 'custom_devise_controllers/registrations#verify_email', :as => :verify_user_email
post "users/update_email" => 'custom_devise_controllers/registrations#update_email', :as => :update_user_email
end
Instead of using after_sign_up_path_for in my Registrations controller, I use after_sign_in_path_for in the application controller. If the number of sign in attempts equals one, that means the user just signed up.
Here is my application_controller.rb:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
def after_sign_in_path_for(resource)
if resource.sign_in_count == 1
verify_user_email_path
else
root_url
end
end
end
So now, whenever someone first signs up, they go to verify_user_email_path. If they are logging in any other time, they go to the root_url.
Thanks, everyone!
Your OmniauthCallbacksController doesn't use the method after_sign_up_path_for so obviously you don't get the redirection you want.
Also, that class_eval thing is completely unnecessary. Here's how that controller should look:
class CustomDeviseControllers::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def all
omniauth = env["omniauth.auth"]
#user = User.from_omniauth(omniauth)
if #user.persisted?
sign_in #user
set_flash_message(:notice, :success, kind: omniauth.provider.capitalize) if is_navigational_format?
redirect_to verify_user_email_path
else
session["devise.#{omniauth.provider}_data"] = omniauth
redirect_to new_user_registration_url
end
end
alias_method :facebook, :all
alias_method :twitter, :all
end

omniauth auth/failure message=invalid_credentials

I am using omiauth with ROR and i get the following error,
omniauth auth/failure message=invalid_credentials,
I am able to connect with linkedin, i am asked for user credentials after giving everything the page redirect is happening and i get the above error.
Here is my callback:
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
def destroy
session[:user_id] = nil
redirect_to root_url, :notice => "Signed out!"
end
end
and the routes.rb is
Lovelinkedin::Application.routes.draw do
root :to => "users#index"
match "/auth/:provider/callback" => "sessions#create"
match "/auth/failure" => "users#index"
match "/signout" => "sessions#destroy", :as => :signout
end
and my omniauth.rb is
Rails.application.config.middleware.use OmniAuth::Builder do
provider :linkedin, 'xxxxx', 'ffffffff'
end
Please help me.
thanks in advance
What is your OmniAuth.config.full_host equal to in your omniauth.rb file?
if its redirecting to a secure site then it should be something like this:
"https://#{SITE_BASE}"
else just take off the s if its not a secure site

Resources