Reputation: 349
While saving the User model, I receive (Validation failed: Uid can't be blank). Uid itself, belongs to the Identity object - which serves as a single identity access point for multiple identity types. In other words, you will be able to use facebook, twitter, or linkedin omniauth strategies within the app without creating multiple accounts. It doesn't seem to work as advertised. Given that I cannot create a user after logging in on LinkedIn from the app.
I tried to swap out the env reference for request.env. Because, it doesn't exist in its current scope. I attempted to locate the error, but couldn't find exactly why the Identity model was not storing the identity before creating the user. It fails right after the User object is created, instead of checking if the User exist w/an identity attached or should be created.
Server development log
Started GET "/users/auth/linkedin" for 127.0.0.1 at 2019-08-03 22:56:03 -0400
I, [2019-08-03T22:56:04.789247 #23148] INFO -- omniauth: (linkedin) Request phase initiated.
Started GET "/users/auth/linkedin/callback?oauth_token=77--2555555-5555-4444-ssfs-dcsfsfsfsfsf93&oauth_verifier=28090" for 127.0.0.1 at 2019-08-03 22:56:31 -0400
I, [2019-08-03T22:56:32.422234 #23148] INFO -- omniauth: (linkedin) Callback phase initiated.
Processing by OmniauthCallbacksController#linkedin as HTML
Parameters: {"oauth_token"=>"77--2555555-5555-4444-ssfs-dcsfsfsfsfsf93", "oauth_verifier"=>"28090"}
Identity Load (1.4ms) SELECT "identities".* FROM "identities" WHERE "identities"."uid" IS NULL AND "identities"."provider" = ? LIMIT ? [["provider", "linkedin"], ["LIMIT", 1]]
↳ app/models/identity.rb:7
(0.2ms) begin transaction
↳ app/models/identity.rb:7
Identity Exists (1.1ms) SELECT 1 AS one FROM "identities" WHERE "identities"."uid" IS NULL AND "identities"."provider" = ? LIMIT ? [["provider", "linkedin"], ["LIMIT", 1]]
↳ app/models/identity.rb:7
(0.2ms) rollback transaction
↳ app/models/identity.rb:7
(0.2ms) begin transaction
↳ app/models/user.rb:47
User Exists (2.0ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = ? LIMIT ? [["email", "[email protected]"], ["LIMIT", 1]]
↳ app/models/user.rb:47
User Create (44.3ms) INSERT INTO "users" ("email", "encrypted_password", "created_at", "updated_at", "confirmed_at") VALUES (?, ?, ?, ?, ?) [["email", "[email protected]"], ["encrypted_password", "$2a"], ["created_at", "2019-08-04 02:56:33.928307"], ["updated_at", "2019-08-04 02:56:33.928307"], ["confirmed_at", "2019-08-04 02:56:33.921311"]]
↳ app/models/user.rb:47
(134.0ms) commit transaction
↳ app/models/user.rb:47
(0.1ms) begin transaction
↳ app/models/user.rb:54
Identity Exists (2.1ms) SELECT 1 AS one FROM "identities" WHERE "identities"."uid" IS NULL AND "identities"."provider" = ? LIMIT ? [["provider", "linkedin"], ["LIMIT", 1]]
↳ app/models/user.rb:54
(0.2ms) rollback transaction
↳ app/models/user.rb:54
Completed 422 Unprocessable Entity in 879ms (ActiveRecord: 191.3ms)
ActiveRecord::RecordInvalid (Validation failed: Uid can't be blank):
app/models/user.rb:54:in `find_for_oauth'
(eval):3:in `linkedin'
omniauth_callbacks_controller.rb
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def self.provides_callback_for(provider)
class_eval %Q{
def #{provider}
@user = User.find_for_oauth(request.env["omniauth.auth"], current_user)
if @user.persisted?
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
[:facebook, :linkedin].each do |provider|
provides_callback_for provider
end
def after_sign_in_path_for(resource)
if resource.email_verified?
super resource
else
finish_signup_path(resource)
end
end
def failure
redirect_to root_path
end
end
identity.rb
class Identity < ApplicationRecord
belongs_to :user
validates_presence_of :uid, :provider
validates_uniqueness_of :uid, :scope => :provider
def self.find_for_oauth(auth)
find_or_create_by(uid: auth.uid, provider: auth.provider)
end
end
user.rb
class User < ApplicationRecord
TEMP_EMAIL_PREFIX = 'change@me'
TEMP_EMAIL_REGEX = /\Achange@me/
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :omniauthable, :trackable, :confirmable
validates_format_of :email, :without => TEMP_EMAIL_REGEX, on: :update
def self.find_for_oauth(auth, signed_in_resource = nil)
# Get the identity and user if they exist
identity = Identity.find_for_oauth(auth)
# If a signed_in_resource is provided it always overrides the existing user
# to prevent the identity being locked with accidentally created accounts.
# Note that this may leave zombie accounts (with no associated identity) which
# can be cleaned up at a later date.
user = if signed_in_resource then
signed_in_resource
else
identity.user
end
# Create the user if needed
if user.nil?
# Get the existing user by email if the provider gives us a verified email.
# If no verified email was provided we assign a temporary email and ask the
# user to verify it on the next step via UsersController.finish_signup
email_is_verified = auth.info.email && (auth.info.verified || auth.info.verified_email)
email = auth.info.email if email_is_verified
user = User.where(:email => email).first if email
# Create the user if it's a new registration
if user.nil?
user = User.new(
name: auth.extra.raw_info.name,
#username: auth.info.nickname || auth.uid,
email: email ? email : "#{TEMP_EMAIL_PREFIX}-#{auth.uid}-#{auth.provider}.com",
password: Devise.friendly_token[0,20]
)
user.skip_confirmation!
user.save!
end
end
# Associate the identity with the user if needed
if identity.user != user
identity.user = user
identity.save! # Where error occurs
end
user
end
def email_verified?
self.email && self.email !~ TEMP_EMAIL_REGEX
end
protected
def confirmation_required?
false
end
end
Expected behavior: A user will successfully log into their Linkedin account and be redirected to the app to complete their registration.
Actual results: The user object creates, and then it fails to save the Identity and leaves the Uid blank.
Upvotes: 2
Views: 1392
Reputation: 1
the error is a validation error, UID is null, that is the problem. So it appears you already have a user record persisted in the DB with a nil UID. Your validation is not allowing you to add another users with a nil UID. So somehow your UID is not getting passed through correctly so your validation will fail. – lacostenycoder Aug 4, 2019 at 5:29
Upvotes: 0