DEV Community

Adam Rogers
Adam Rogers

Posted on

Creating and updating users with omniauth

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
Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
honzasterba profile image
Honza Štěrba

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.

Collapse
 
rodreegez profile image
Adam Rogers

Absolutely. If your app supports logging into a single account from multiple providers you’re gonna have a bad time using this implementation.

Collapse
 
oyenmwen profile image
Osayimwen Odia

a possible workaround could be:

unless user.password.present?
    user.password = Devise.friendly_token[0, 20]
end
Enter fullscreen mode Exit fullscreen mode