I have an app, build on Rails 4.2 and tried to do twitter authentication, using RailsCasts #241 Simple OmniAuth.
But have this issue: Validation failed: Password can't be blank!
I searched answer everywhere, but did not find the solution!
user.rb
class User < ActiveRecord::Base
has_secure_password
validates :password, length: { minimum: 6 }, allow_blank: true
def self.create_with_omniauth(auth)
create! do |user|
user.provider = auth.provider
user.uid = auth.uid
user.name = auth.info.name
end
end
end
sessions_controller.rb
class SessionsController < ApplicationController
def login
end
def create
#user = User.find_by_email(params[:email])
if #user && #user.authenticate(params[:password])
session[:user_id] = #user.id
redirect_to root_path
else
redirect_to :back
end
end
def create_session
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 logout
reset_session
redirect_to root_path
end
end
routes.rb
get 'sessions/login'
get 'sessions/logout'
post 'sessions' => 'sessions#create'
get '/auth/:provider/callback' => 'sessions#create_session'
post '/auth/:provider/callback' => 'sessions#create_session'
get 'registration' => 'users#new', as: 'registration'
Solution
After editing user.rb model looks like:
class User < ActiveRecord::Base
has_secure_password
validates :password, length: { minimum: 6 }
require 'securerandom'
def self.create_with_omniauth(auth)
create! do |user|
user.provider = auth.provider
user.uid = auth.uid
user.name = auth.info.name
user.password = SecureRandom.urlsafe_base64
end
end
end
An alternative approach is to enter a random password on user creation. For example as shown in the omniauth-google-oauth2 readme. So you could alter your code to this:
require 'securerandom'
def self.create_with_omniauth(auth)
create! do |user|
user.provider = auth.provider
user.uid = auth.uid
user.name = auth.info.name
user.password = SecureRandom.urlsafe_base64
end
end
I think it's problem with
has_secure_password
try commenting it, and you can override the has_secure_password Or
has_secure_password(validations: false)
This is happening as it automatically adds
validates_presence_of :password_digest
So when specify allow_blank or allow_null it will not work
Related
Trying to follow this tutorial to implement Google Sign In With Devise into my rails app: https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview
Getting this error:
My routes:
devise_for :users, controllers: { sessions: 'users/sessions', registrations: 'users/registrations', omniauth_callbacks: 'users/omniauth_callbacks' }
Omniauthable controller: app/controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def google_oauth2
#user = User.from_omniauth(request.env["omniauth.auth"])
puts #user
if #user.persisted?
sign_in_and_redirect #user
set_flash_message(:notice, :success, kind: "Google") if is_navigational_format?
else
puts "WENT INTO HERE---------"
session["devise.google_data"] = request.env["omniauth.auth"]
redirect_to root_path
end
end
def failure
redirect_to root_path
end
end
User.rb
class User < ApplicationRecord
devise :rememberable, :trackable,
:omniauthable, omniauth_providers: %i[google_oauth2]
# rubocop:disable Metrics/AbcSize
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
user.email = auth.info.email
# user.password = Devise.friendly_token[0,20]
user.first_name = auth.info.first_name
user.last_name = auth.info.last_name
user.image_url = auth.info.image # assuming the user model has an image
end
end
end
I have identified the 13 arguments that the app is referring to, from the session dump:
warden.user.user.key: {"id"=>204, "uid"=>"*******", "first_name"=>"First name", "last_name"=>"last name", "provider"=>"google_oauth2", "image_url"=>"*****", "email"=>"***", "has_logged_in"=>false, "created_at"=>"2018-03-17T21:26:24.782Z", "updated_at"=>"2018-03-17T23:31:38.220Z", "admin"=>false}
But I don't know where this came from or how to solve it
Not sure what the issue was but after closing my browser, the problem was fixed. So it probably had something to do with my cache.
So I'm trying to integrate Google OAuth2 with devise in my app but after signing in and allowing access to the app I get redirected back to the sign in page for some reason. I'm using the following tutorial:
https://www.digitalocean.com/community/tutorials/how-to-configure-devise-and-omniauth-for-your-rails-application
Here are my files:
callbacks_controller.rb
class CallbacksController < Devise::OmniauthCallbacksController
def google_oauth2
# You need to implement the method below in your model (e.g. app/models/user.rb)
#user = User.from_omniauth(request.env["omniauth.auth"])
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
end
route.rb:
devise_for :users, :controllers => { :omniauth_callbacks => "callbacks" }
users.rb:
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
user.provider = auth.provider
user.uid = auth.uid
user.email = auth.info.email
user.password = Devise.friendly_token[0,20]
end
end
devise.rb:
config.omniauth :google_oauth2, ENV['GOOGLE_CLIENT_ID'], ENV['GOOGLE_CLIENT_SECRET'], callback_url: ENV['GOOGLE_CALLBACK_URL']
What am I doing wrong? Thanks
Try this:
class ApplicationController < ActionController::Base
def after_sign_in_path_for(resource)
# return path you want to go to...
end
end
I'm trying to use OmniAuth to integrate Facebook with my website, and I think I'm getting a few errors here. Right now when I click "Sign in with Facebook" it does bring me to Facebook, but soon as I sign in I get an error saying ActiveModel::ForbiddenAttributesError. Also, I think there might be an issue my routes as well but I'm not sure.
Also, I followed this RailsCasts tutorial: http://railscasts.com/episodes/360-facebook-authentication?autoplay=true
Edit: The error is on this line here, where(auth.slice(:provider, :uid)).first_or_initialize.tap do |user|
omniauth.rb
OmniAuth.config.logger = Rails.logger
Rails.application.config.middleware.use OmniAuth::Builder do
provider :facebook, ENV['FACEBOOK_APP_ID'], ENV['FACEBOOK_SECRET']
end
user.rb
class User < ActiveRecord::Base
def self.from_omniauth(auth)
where(auth.slice(:provider, :uid)).first_or_initialize.tap do |user|
user.provider = auth.provider
user.uid = auth.uid
user.name = auth.info.name
user.oauth_token = auth.credentials.token
user.oauth_expires_at = Time.at(auth.credentials.expires_at)
user.save!
end
end
end
routes.rb
Rails.application.routes.draw do
get 'auth/:provider/callback', to: 'sessions#create'
get 'auth/failure', ('/posts/index')
get 'signout', to: 'sessions#destroy', as: 'signout'
resources :welcome
resources :posts
root "welcome#index"
sessions_controller.rb
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
application_controller.rb
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
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
helper_method :current_user
end
Modify your finder like so:
class User < ActiveRecord::Base
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_initialize do |user|
user.provider = auth.provider
user.uid = auth.uid
user.name = auth.info.name
user.oauth_token = auth.credentials.token
user.oauth_expires_at = Time.at(auth.credentials.expires_at)
user.save!
end
end
end
I'm attempting to write a test for the creation of a session with the omniauth-google-oauth2 gem. Do I need to pass the env["omniauth.auth"] variable with the post :create? Perhaps when I was attempting to do that I was doing it incorrectly. The error I'm getting is shown below...
Rake Test Error
1) Error:
SessionsControllerTest#test_should_get_create:
NoMethodError: undefined method `provider' for nil:NilClass
app/models/user.rb:6:in `from_omniauth'
app/controllers/sessions_controller.rb:4:in `create'
test/controllers/sessions_controller_test.rb:13:in `block in <class:SessionsControllerTest>'
The following is my attempt at writing the test...
SessionsControllerTest
require 'test_helper'
class SessionsControllerTest < ActionController::TestCase
setup :prepare_omniauth
test "should get create" do
post :create
redirect_to root_path, notice: "Signed in!"
end
test "should get destroy" do
get :destroy
assert session[:user_id].blank?, "user_id should no longer exist"
assert_redirected_to root_path, notice: "Signed out!"
end
private
def prepare_omniauth
OmniAuth.config.test_mode = true
OmniAuth.config.mock_auth[:google] = OmniAuth::AuthHash.new({
:provider => 'google',
:uid => '123545'
})
request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:google]
end
end
Sessions Controller
class SessionsController < ApplicationController
def create
user = User.from_omniauth(env["omniauth.auth"])
session[:user_id] = user.id
redirect_to root_path
end
def destroy
session[:user_id] = nil unless session[:user_id].blank?
redirect_to root_path
end
end
User Model
class User < ActiveRecord::Base
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_initialize.tap do |user|
user.provider = auth.provider
user.uid = auth.uid
user.name = auth.info.name
user.oauth_token = auth.credentials.token
user.oauth_expires_at = Time.at(auth.credentials.expires_at)
user.save!
end
end
end
I believe this blog has the answer to your question: http://natashatherobot.com/rails-test-omniauth-sessions-controller/
Basically you need to edit your rails_helper/spec_helper to set Omniauth's test mode to true and create an omniauth_hash to be used in your tests:
OmniAuth.config.test_mode = true
omniauth_hash = { 'provider' => 'github',
'uid' => '12345',
'info' => {
'name' => 'natasha',
'email' => 'hi#natashatherobot.com',
'nickname' => 'NatashaTheRobot'
},
'extra' => {'raw_info' =>
{ 'location' => 'San Francisco',
'gravatar_id' => '123456789'
}
}
}
OmniAuth.config.add_mock(:github, omniauth_hash)
Then you require it before any tests:
before do
request.env['omniauth.auth'] = OmniAuth.config.mock_auth[:github]
end
Now you should be able to create users using Omniauth like in this example:
describe SessionsController do
it "should successfully create a user" do
expect {
post :create, provider: :github
}.to change{ User.count }.by(1)
end
end
All credits to Natasha from http://natashatherobot.com for posting this answer in her blog.
I am having a little trouble with Twitter authentication. I keep getting the email can't be blank, and redirect to sign up, but after inserting email and clicking sign up, I still get the same error. I tried making it unneeded, but I get the error saying that someone with "" email exists already.
Thanks.
Devise Routes.rb
def has_role?(role)
return true;
end
def self.from_omniauth(auth)
where(auth.slice(:provider, :uid)).first_or_create do |user|
user.provider = auth.provider
user.uid = auth.uid
user.username = auth.info.nickname
end
end
def self.new_with_session(params, session)
if session["devise.user_attributes"]
new(session["devise.user_attributes"]) do |user|
user.attributes = params
user.valid?
end
else
super
end
end
def password_required?
super && provider.blank?
end
def update_with_password(params, *options)
if encrypted_password.blank?
update_attributes(params, *options)
else
super
end
end
end
Call Back Controller
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def all
user = User.from_omniauth(request.env["omniauth.auth"])
if user.persisted?
flash.notice = "Signed in!"
sign_in_and_redirect user
else
session["devise.user_attributes"] = user.attributes
redirect_to new_user_registration_url
end
end
alias_method :twitter, :all
end
You can define username to be your "authentication key" instead of email by
uncommenting this line
config.authentication_keys = [ :email ]
in config/initializers/devise.rb
and changing it to
config.authentication_keys = [ :username ]
i also had the same problem.
this error arises due the fact that in devise User model Email field is set to NotNull.
Solutions:-
1.Set email field in devise to allow null values.
2.so i had saved the email from twitter in my devise email field this is the code for twitter
def self.find_for_twitter_oauth(auth, signed_in_resource=nil)
user = User.where(:provider => auth.provider, :uid => auth.uid).first
unless user
temp = Tempfile.new([auth["uid"], ".jpg"])
temp.binmode
temp.write(open(auth["info"]["image"]).read)
user = User.create(name:auth.extra.raw_info.name,
provider:auth.provider,
uid:auth.uid,
email:auth.info.email,
password:Devise.friendly_token[0,20],
image:temp
)
user.build_profile(name:user.name,image:temp)
user.profile.save
end
user
end