There are a number of tutorials, including the omniauth overview, that suggest having a method along the following lines:
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.name = auth.info.name # assuming the user model has a name
user.image = auth.info.image # assuming the user model has an image
end
end
That first_or_create
will try to find a user by the auth.provider
(e.g. twitter
) and auth.uid
(e.g. rodreegez
) and create one with those attributes if one isn't found. What it doesn't do is update a found user if the user is found.
So, in the example above if, when an existing user signs in with Twitter having updated their name, our app won't get the new name of the user.
Instead, we should do something like this to ensure we are updating our user information when the user logs in:
def self.from_omniauth(auth)
user = find_or_initialize_by(provider: auth.provider, uid: auth.uid)
user.email = auth.info.email
user.password = Devise.friendly_token[0, 20]
user.name = auth.info.name # assuming the user model has a name
user.image = auth.info.image # assuming the user model has an image
user.save
user
end
That find_or_initialize_by
behaves more like we expect it in this situation. Or at least, it behaves in a way that is more appropriate to the task we have at hand - it either finds an existing record or initializes a new one, but doesn't try to save the record. We are then able to set the attributes of the found-or-initialized user, save it, and return the saved-or-updated user to the caller.
Hope that helps.
🍻
Top comments (3)
You should only update the fields that are necessary, in the scenario when the user has signed up using facebook and later also configured a password you will throw the password away by using this code and it could lead to some confused users having to reset their password for no reason.
Absolutely. If your app supports logging into a single account from multiple providers you’re gonna have a bad time using this implementation.
a possible workaround could be: