Omniauth2 Google Record Invalid Error using Devise - ruby-on-rails

I'm using Devise with Google Omniauth, but I keep getting User Record Invalid error:
I only have this validation in my User model:
validates :email, presence: true, uniqueness: true
User model
devise :omniauthable, omniauth_providers: [:google_oauth2]
def self.from_google(email:, full_name:, uid:, avatar_url:)
create_with(uid: uid, full_name: full_name, avatar_url: avatar_url).find_or_create_by!(email: email)
end
My routes
devise_for :users, :controllers => { omniauth_callbacks: 'users/omniauth_callbacks', :registrations => "registrations"}
omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def google_oauth2
user = User.from_google(from_google_params)
if user.present?
sign_out_all_scopes
flash[:success] = t 'devise.omniauth_callbacks.success', kind: 'Google'
sign_in_and_redirect user, event: :authentication
else
flash[:alert] = t 'devise.omniauth_callbacks.failure', kind: 'Google', reason: "#{auth.info.email} is not authorized."
redirect_to new_user_session_path
end
end
protected
def after_omniauth_failure_path_for(_scope)
new_user_session_path
end
def after_sign_in_path_for(resource_or_scope)
stored_location_for(resource_or_scope) || root_path
end
private
def from_google_params
#from_google_params ||= {
uid: auth.uid,
email: auth.info.email,
full_name: auth.info.name,
avatar_url: auth.info.image
}
end
def auth
#auth ||= request.env['omniauth.auth']
end
end
Sessions controller
class Users::SessionsController < Devise::SessionsController
def after_sign_out_path_for(_resource_or_scope)
new_user_session_path
end
def after_sign_in_path_for(resource_or_scope)
stored_location_for(resource_or_scope) || root_path
end
end
Any clue on how to trace or fix this? It's driving me crazy! Thanks

Devise tries to display a relevant error message for the Spanish-translated app of your site. You are missing a translation for the Spanish error message.
Go to config/locales/es.yml and add in:
es:
activerecord:
errors:
messages:
record_invalid:
"Spanish error message comes here"

Related

overriding Devise update action inside omniauth_callbacks_controller

References:
Rails 5.0.1
omniauth 1.8.1
omniauth-facebook 5.0.0
I have users logged in with email + password and others with facebook account.
The main issue is to make possible to facebook's users edit their own account without needing to use the password, because they just don't have. The solution in this post so I can add this class:
def update
if current_user.provider == "facebook"
params.delete("current_password")
resource.update_without_password(user_params)
else
resource.update_with_password(user_params)
end
redirect_to root_path
end
So I have this:
routes.br
devise_for :users, controllers: { omniauth_callbacks: "omniauth_callbacks" }
and inside the omniauth_callbacks_controller.rb I add this update function:
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def update
if current_user.provider == "facebook"
params.delete("current_password")
resource.update_without_password(user_params)
else
resource.update_with_password(user_params)
end
redirect_to root_path
end
def facebook
user = User.find_for_facebook_oauth(request.env['omniauth.auth'])
if user.persisted?
sign_in user
redirect_to root_path
set_flash_message(:notice, :success, kind: 'Facebook') if is_navigational_format?
else
session['devise.facebook_data'] = request.env['omniauth.auth']
redirect_to new_user_registration_url
end
end
def user_params
params.require(:user).permit(:first_name, :last_name, :phone, :picture, :facebook_picture_url)
end
end
The problem is that the devise doesn't read this function. So if we add a byebug inside it will not stop there.
The only way to override this update function is to add this configurations below.
routes.br
devise_for :users, controllers: { registrations: 'registrations' }
registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
def update
if current_user.provider == "facebook"
params.delete("current_password")
resource.update_without_password(user_params)
else
resource.update_with_password(user_params)
end
redirect_to root_path
end
def facebook
user = User.find_for_facebook_oauth(request.env['omniauth.auth'])
if user.persisted?
sign_in user
redirect_to root_path
set_flash_message(:notice, :success, kind: 'Facebook') if is_navigational_format?
else
session['devise.facebook_data'] = request.env['omniauth.auth']
redirect_to new_user_registration_url
end
end
def user_params
params.require(:user).permit(:first_name, :last_name, :phone, :picture, :facebook_picture_url)
end
end
The only problem with this second approach is that if I try to login with facebook I receive this error message:
The action 'facebook' could not be found for
Devise::OmniauthCallbacksController
My devise configuration:
devise.rb
Devise.setup do |config|
config.omniauth :facebook, ENV["FB_ID"], ENV["FB_SECRET"],
scope: 'email',
info_fields: 'email, first_name, last_name',
image_size: 'large', # 50x50, guaranteed ratio
secure_image_url: true
...
end
Just in case somebody else has a similar problem. The thing was that the routes.rb needed to specify both of the controllers being overwritten, like:
devise_for :users, controllers: { registrations: 'registrations' , omniauth_callbacks: 'omniauth_callbacks' }

Devise Wrong number of arguments (13 for 2) upon Omniauthable

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.

LoadError in OmniauthCallbacksController#passthru (with devise/stripe connect)

Trying to implement Stripe Connect, and am getting the following error when I click the "connect to stripe" button.
The action 'passthru' could not be found for OmniauthCallbacksController
users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def stripe_connect
#user = current_user
if #user.update_attributes({
provider: request.env["omniauth.auth"].provider,
uid: request.env["omniauth.auth"].uid,
access_code: request.env["omniauth.auth"].credentials.token,
publishable_key: request.env["omniauth.auth"].info.stripe_publishable_key
})
# anything else you need to do in response..
sign_in_and_redirect #user, :event => :authentication
set_flash_message(:notice, :success, :kind => "Stripe") if is_navigational_format?
else
session["devise.stripe_connect_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
end
models/user.rb
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable, :omniauth_providers => [:stripe_connect]
routes.rb
devise_for :users, controllers: { registrations: 'users/registrations', :omniauth_callbacks => "users/omniauth_callbacks" }
gemfile.rb
gem 'omniauth-stripe-connect'
initializers/stripe.rb
Rails.configuration.stripe = {
:publishable_key => ENV['PUBLISHABLE_KEY'],
:secret_key => ENV['SECRET_KEY']
}
Stripe.api_key = Rails.configuration.stripe[:secret_key]
initializers/devise.rb
config.omniauth :stripe_connect,
ENV['STRIPE_CONNECT_CLIENT_ID'],
ENV['STRIPE_SECRET_KEY'],
:scope => 'read_write',
:stripe_landing => 'register'
button link:
<%= link_to image_tag('blue-on-light.png'), user_stripe_connect_omniauth_authorize_path(:stripe_connect) %>
As I understand it with my noob Ruby mind, I need to define 'passthru'? how do I define it though? when I enter:
def passthru
end
the link doesn't work / the page reloads itself. Haven't been able to find a solution on here. What am I missing?
EDIT:
Changed my connect to stripe link to:
<%= link_to image_tag('blue-on-light.png'), "/users/auth/stripe_connect" %>
The link takes me to the connect to stripe page, but when I click the "connect to stripe" button, the page cant be found, and doesn't load or redirect.
Can you try changing
# app/controllers/omniauth_callbacks_controller.rb
class OmniauthCallbacksController < ApplicationController
def stripe_connect
....
to
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def stripe_connect
#user = User.find_for_stripe_connect(request.env['omniauth.auth'], current_user)
set_notice_and_redirect
end
private
def set_notice_and_redirect
if #user.persisted?
flash[:notice] = 'Successfully signed in'
set_flash_message(:notice, :success, :kind => "Stripe") if is_navigational_format?
else
session["devise.stripe_connect_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
end
and in your user model
# Checks if user exists, otherwise create it
def self.find_for_stripe_connect(access_token, _ = nil)
data = access_token.info
user = User.where(email: data['email']).first_or_create(
email: data['email'],
password: Devise.friendly_token[0, 20],
provider: request.env["omniauth.auth"].provider,
uid: request.env["omniauth.auth"].uid,
access_code: request.env["omniauth.auth"].credentials.token,
publishable_key: request.env["omniauth.auth"].info.stripe_publishable_key
)
user
end
and also sing in path
<%= link_to image_tag('blue-on-light.png'), user_stripe_connect_omniauth_authorize %>
I think you don't need to define a passthru action. If you see the below two in the routes it can work. Authorize path is for redirecting user to stripe and callback is for redirecting user from stripe back to your site
$ rake routes
user_stripe_connect_omniauth_authorize /auth/stripe_connect(.:format) ....
user_stripe_connect_omniauth_callback /auth/stripe_connect/callback(.:format) ....

ERR_TOO_MANY_REDIRECTS logging in with OmniAuth/Devise/Rails

I'm attempting to add Facebook login to my Rails 4 site following these instructions:
https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview
I'm currently using Devise and Pundit for authorization and authentication. Despite having followed the instructions to my best ability, I'm getting an error. When I click my "Login with Facebook" button, a window pops up that asks for email/password, and when I submit that info, I get an error page that reads:
[MyApp.com] page isn’t working
[MyApp.com] redirected you too many times.
Try:
Reloading the page
Clearing your cookies ERR_TOO_MANY_REDIRECTS
It seems like somehow I've introduced a redirect loop, but I don't really understand the data flow, so it's hard to find where I've gone wrong.
Here's my routes.rb:
Rails.application.routes.draw do
get 'home/index'
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks", sessions: "sessions" }
resources :movies
root 'home#index'
end
omniauth_callbacks_controller.rb:
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
before_filter :authenticate_user, :except => [:new, :create, :destroy]
def facebook
# 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?
sign_in_and_redirect #user, :event => :authentication #this will throw if #user is not activated
set_flash_message(:notice, :success, :kind => "Facebook") if is_navigational_format?
else
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
def failure
redirect_to root_path
end
end
OmniAuth's configuration in config/initializers/devise.rb:
config.omniauth :facebook, '[APP ID]', '[APP SECRET]', callback_url: "https://#{ENV['C9_HOSTNAME']}/users/auth/facebook",
:client_options => {:ca_file => '/usr/lib/ssl/certs/ca-certificates.crt'}
My user model (user.rb):
class User < ActiveRecord::Base
rolify
# Include default devise modules. Others available are:
# :confirmable, :lockable, and :timeoutable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:omniauthable, :omniauth_providers => [:facebook]
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]
end
end
def self.new_with_session(params, session)
super.tap do |user|
if data = session["devise.facebook_data"] && session["devise.facebook_data"]["extra"]["raw_info"]
user.email = data["email"] if user.email.blank?
end
end
end
end
And the link in my view (using HAML):
%button
= link_to "Log in with Facebook", user_omniauth_authorize_path(:facebook)
I've just had this exact problem too! After you recieved this error, were you able to reload the page and it had actually logged you in? If so, then that's what I had. The log in worked - but the redirects weren't good.
Have you added in the function after_sign_in_path_for(resource) to the application_controller.rb that redirects the page after login? I had and that's why the redirects were occuring.
My solution was to fixed the if statement to include the right referrer. I just added in || request.referer.include?("google"), of course you may have to change it to account for 'facebook' in the referrer url.
def after_sign_in_path_for(resource)
sign_in_url = new_user_session_url
if request.referer == sign_in_url || request.referer.include?("google")
super
goals_path
else
stored_location_for(resource) || request.referer || root_path
end
end
Now... if you haven't used that function then my answer will be useless to you. Good luck.

Error Chapter 10 Rails

Hi I'm working on the tutorial and stumped on this error. I have tried to create a controller "create_reset_digest" but still working. I'm pretty new to rails. Thanks:
ERROR["test_password_resets", PasswordResetsTest, 3.338488]
test_password_resets#PasswordResetsTest (3.34s)
NoMethodError: NoMethodError: undefined method `reset_digest=' for #<User:0x007fed452318f8>
app/models/user.rb:57:in `create_reset_digest'
app/controllers/password_resets_controller.rb:12:in `create'
test/integration/password_resets_test.rb:17:in `block in <class:PasswordResetsTest>'
app/models/user.rb:57:in `create_reset_digest'
app/controllers/password_resets_controller.rb:12:in `create'
test/integration/password_resets_test.rb:17:in `block in <class:PasswordResetsTest>'
Here is the code that I'm having issues with:
---user.rb
class User < ActiveRecord::Base
attr_accessor :remember_token, :activation_token, :reset_token
before_save :downcase_email
before_create :create_activation_digest
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, length: { minimum: 6 }, allow_blank: true
# Returns the hash digest of the given string.
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
# Returns a random token.
def User.new_token
SecureRandom.urlsafe_base64
end
# Remembers a user in the database for use in persistent sessions.
def remember
self.remember_token = User.new_token
update_attribute(:remember_digest, User.digest(remember_token))
end
# Forgets a user.
def forget
update_attribute(:remember_digest, nil)
end
# Returns true if the given token matches the digest.
def authenticated?(attribute, token)
digest = send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
# Activates an account.
def activate
update_attribute(:activated, true)
update_attribute(:activated_at, Time.zone.now)
end
# Sends activation email.
def send_activation_email
UserMailer.account_activation(self).deliver_now
end
# Sets the password reset attributes.
def create_reset_digest
self.reset_token = User.new_token
update_attribute(:reset_digest, User.digest(reset_token))
update_attribute(:reset_sent_at, Time.zone.now)
end
# Sends password reset email.
def send_password_reset_email
UserMailer.password_reset(self).deliver_now
end
# Returns true if a password reset has expired.
def password_reset_expired?
reset_sent_at < 2.hours.ago
end
private
# Converts email to all lower-case.
def downcase_email
self.email = email.downcase
end
# Creates and assigns the activation token and digest.
def create_activation_digest
self.activation_token = User.new_token
self.activation_digest = User.digest(activation_token)
end
end
--password_resets_controller.rb
class PasswordResetsController < ApplicationController
before_action :get_user, only: [:edit, :update]
before_action :valid_user, only: [:edit, :update]
before_action :check_expiration, only: [:edit, :update]
def new
end
def create
#user = User.find_by(email: params[:password_reset][:email].downcase)
if #user
#user.create_reset_digest
#user.send_password_reset_email
flash[:info] = "Email sent with password reset instructions"
redirect_to root_url
else
flash.now[:danger] = "Email address not found"
render 'new'
end
end
def edit
end
def update
if both_passwords_blank?
flash.now[:danger] = "Password/confirmation can't be blank"
render 'edit'
elsif #user.update_attributes(user_params)
log_in #user
flash[:success] = "Password has been reset."
redirect_to #user
else
render 'edit'
end
end
private
def user_params
params.require(:user).permit(:password, :password_confirmation)
end
# Returns true if password & confirmation are blank.
def both_passwords_blank?
params[:user][:password].blank? &&
params[:user][:password_confirmation].blank?
end
# Before filters
def get_user
#user = User.find_by(email: params[:email])
end
# Confirms a valid user.
def valid_user
unless (#user && #user.activated? &&
#user.authenticated?(:reset, params[:id]))
redirect_to root_url
end
end
# Checks expiration of reset token.
def check_expiration
if #user.password_reset_expired?
flash[:danger] = "Password reset has expired."
redirect_to new_password_reset_url
end
end
end
---password_resets_test.rb
require 'test_helper'
class PasswordResetsTest < ActionDispatch::IntegrationTest
def setup
ActionMailer::Base.deliveries.clear
#user = users(:michael)
end
test "password resets" do
get new_password_reset_path
assert_template 'password_resets/new'
# Invalid email
post password_resets_path, password_reset: { email: "" }
assert_not flash.empty?
assert_template 'password_resets/new'
# Valid email
post password_resets_path, password_reset: { email: #user.email }
assert_not_equal #user.reset_digest, #user.reload.reset_digest
assert_equal 1, ActionMailer::Base.deliveries.size
assert_not flash.empty?
assert_redirected_to root_url
# Password reset form
user = assigns(:user)
# Wrong email
get edit_password_reset_path(user.reset_token, email: "")
assert_redirected_to root_url
# Inactive user
user.toggle!(:activated)
get edit_password_reset_path(user.reset_token, email: user.email)
assert_redirected_to root_url
user.toggle!(:activated)
# Right email, wrong token
get edit_password_reset_path('wrong token', email: user.email)
assert_redirected_to root_url
# Right email, right token
get edit_password_reset_path(user.reset_token, email: user.email)
assert_template 'password_resets/edit'
assert_select "input[name=email][type=hidden][value=?]", user.email
# Invalid password & confirmation
patch password_reset_path(user.reset_token),
email: user.email,
user: { password: "foobaz",
password_confirmation: "barquux" }
assert_select 'div#error_explanation'
# Blank password & confirmation
patch password_reset_path(user.reset_token),
email: user.email,
user: { password: " ",
password_confirmation: " " }
assert_not flash.empty?
assert_template 'password_resets/edit'
# Valid password & confirmation
patch password_reset_path(user.reset_token),
email: user.email,
user: { password: "foobaz",
password_confirmation: "foobaz" }
assert is_logged_in?
assert_not flash.empty?
assert_redirected_to user
end
end
Well, I ran into a very similar problem at chapter 10 with create_activation_digest in app/models/user.rb.
My problem was assigning value to a non-existing attribute:
In the tutorial it says
self.activation_token = User.new_token.
but activation_token is not an attribute of User.
Hence, make sure reset_digest is really an attribute of the User table.
Have a look at the migration files, and run
rake db:migrate
if this attribute appears.

Resources