6

I’m using Rails 4.2.5 with the “omniauth-google-oauth2” gem. In my application, the only way users will be able to sign in is through their Google or Facebook logins. What I would like is to pre-populate my application with an initial user, me (email = ‘davea@gmail.com”), with the admin role. It would be nice to do this programmatically so that when I roll this out to other environments I can use the same code.

My roles table (through db/seeds.rb) has the roles

Admin
User

and my app/model/user.rb file has

def self.from_omniauth(auth)
  where(auth.slice(:provider, :uid)).first_or_initialize.tap do |user|
    user.provider = auth.provider
    user.uid = auth.uid
    user.name = auth.info.name
    user.oauth_token = auth.credentials.token
    user.oauth_expires_at = Time.at(auth.credentials.expires_at)
    user.save!
  end
end

I’m not sure how to do what I want, however, and so would value some counsel.

Dave
  • 15,639
  • 133
  • 442
  • 830

2 Answers2

2

Now assume you have your Google uid. Just create a user from your seeds, e.g.:

user = User.new(
  provider: "google",
  uid: "your-google-id",
  email: "davea@gmail.com",
  name: "Your name"
)
user.roles << admin_role # Replace this line with your role assignment
user.save # Perhaps use save(validate: false) if there're validations for other fields

With that, when you log in with Google, the omniauth logic should be able to find the seed user, which means you'll be able to act as admin.

Please do note this assumes you won't need the Google oauth token to do any further operation as you don't have that saved, and from your from_omniauth it doesn't save if the user record exists already.

P.S. from your example code, Oauth info is saved directly to User model (provider and uid). With that, I'm afraid a user won't be able to log in with Facebook and Google at the same time as both would want to save to these two fields.

Update: pasting a model from my codebase that is a separate model from User which allows multiple providers login. Of course the controller needs to update to use Authorization instead of User. Just in case it helps.

class Authorization < ActiveRecord::Base
  belongs_to :user

  def self.from_omniauth(auth)
    authorization = where(auth.slice(:provider, :uid)).first_or_create
    return authorization if authorization.user

    if user = User.where(email: auth.info.email).first
      authorization.bind_user(user)
    else
      user = authorization.create_user(auth.info)
    end

    authorization
  end

  def bind_user(user)
    self.user = user
    save
  end

  def create_user(info)
    user = User.new(
      email:      info.email,
      password:   Devise.friendly_token[0, 20],
      first_name: info.first_name,
      last_name:  info.last_name,
    )
    user.save(validate: false)

    bind_user(user)

    user
  end
end
James Chen
  • 10,794
  • 1
  • 41
  • 38
  • Hi, That's fine if a user can't be simultaneously logged into both Google and Facebook and in my app. I am confused about why you moved the self.from_omniauth from the User model into the Authorization model. Is this so duplicate users don't get created if my database already contains a user matching the provider and uid? – Dave May 09 '16 at 18:50
  • @Dave Yes the purpose of Authorization model is to support multiple providers for the same user. Without that (from your code) a user will be created twice when logged in with Facebook and Google. It's a not big issue, but if you also retrieve email from provider and store that to users table while having email as login identifier as well it causes issues. – James Chen May 09 '16 at 22:27
1

You will have to run a request through your controller through your seed.rb file in order to execute the OAuth2 process.

Since you will most likely have to enter in credentials or select your google account from a GUI, I suggest running a system command in your seed.rb file that opens a browser to the url of your authorize action.

# Mac:
system("open <url_to_authorize_action>")

If this needs to be serialized, immediately after, add a while loop that checks the DB every N time threshold to see if that user is authorized.

while <user_not_authorized> do
    sleep <N seconds>
end

You could roll this to multiple dev environments but obviously not production.

  • I would like to do something automated, that doesn't require user interaction, other than running a file perhaps. Cna I take advantage of the fact that I know my Google ID ahead of time? – Dave May 03 '16 at 21:25