loading...

Creating and updating users with omniauth

rodreegez profile image Adam Rogers ・2 min read

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.

🍻

Discussion

pic
Editor guide