Scott Martin
Scott Martin

Reputation: 640

Getting Rails 3.1's has_secure_password to work well with OmniAuth

I'm porting a project from Rails 3 to 3.1. My authentication system was easily switched over to the new has_secure_password in ActiveRecord. The only problem I'm running into is that I also use OmniAuth and I have the system set up so if a user signs up using one of the OmniAuth providers the account shouldn't require a password. I can't seem to override the password_digest validations setup by has_secure_password. Is there anyway to turn off those validations and write my own, or am I going to have to just use my old hand written bcrypt functions from my Rails 3 version of the site?

Upvotes: 8

Views: 2463

Answers (2)

crankin
crankin

Reputation: 41

Scott, your idea is correct. I've been wrestling with this problem to no avail. I tried to override 'has_secure_password' and it simply won't work. No matter where I stuck the code.

Instead I have the following:

class User < ActiveRecord::Base
  has_secure_password

  validates_presence_of :password, :on => :create, :if => :password_required

  # Associations
  has_many :authentications

  # Callbacks
  before_validation :no_password_omniauth

  # Gets set to true if the caller is trying to authenticate with omniauth.
  @called_omniauth = false

  # Build new omniauth users
  def apply_omniauth(omniauth)
    authentications.build(
    :provider => omniauth['provider'], 
    :uid => omniauth['uid'])
    self.first_name = omniauth['user_info']['first_name'] if self.first_name.blank?
    self.last_name = omniauth['user_info']['last_name'] if self.last_name.blank?
    self.email = omniauth['user_info']['email'] if omniauth['user_info']['email'] && self.email.blank?
    @called_omniauth = true
  end

  def password_required
    return false if @called_omniauth == true
    (authentications.empty? || !password.blank?)
  end

  private

  def no_password_omniauth
    self.password_digest = 0 unless password_required
  end

end

The apply_omniauth method gets called from the controller when someone is trying to authenticate or sign up.

Thanks for the idea you nailed it.

Upvotes: 4

Scott Martin
Scott Martin

Reputation: 640

I ended up going back to using custom methods. However, I did realize afterwards that I should've been able to use a before_validation callback to check the conditions, then if they matched set the password_digest to anything simple like '0'. This way the digest would never be blank, but at the same time it shouldn't ever validate as a correct password, making them sign in through OmniAuth.

Feel free to correct me if I'm mistaken.

Upvotes: 5

Related Questions