1
votes

I'm using devise, omniauth and client-side-validation like described in ryan bates railscasts.

I'm now facing a problem with the password validation which should be omitted when registering via omniauth 3rd party provider.

Registration form (html.erb):

Sign up

Register via 3rd party networks

<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :validate => true) do |f| %>

Register directly

<%= f.label :firstname %> <%= f.text_field :firstname %>

<%= f.label :lastname %> <%= f.text_field :lastname %>

<%= f.label :email %> <%= f.text_field :email %>

<%= f.label :password %> <%= f.password_field :password %>

<%= f.label :password_confirmation %> <%= f.password_field :password_confirmation %>

<%= f.submit "Register now", :class => "button-big" %>

<% end %>

My user model has a - see UPDATE below

validates :password, :presence => true, :confirmation =>true

clause and the password_required? definition

def password_required?
(authentications.empty? || !password.blank?)
end

When I'm registering via omniauth 3rd party provider the registration form correctly pops up and the user is asked to enter an email address. Unfortunately the user has to enter a password although he shouldn't be prompted due to registration via 3rd party.

Can anybody give me a hint, how to accomplish?

Thanks and best regards

Jan

UPDATE: to give a more specific view I added some more code snippets

AuthenticationsController:

class AuthenticationsController < ApplicationController
def index
@authentications = current_user.authentications if current_user
end

def create
omniauth = request.env["omniauth.auth"]
authentication = Authentication.find_by_provider_and_uid(omniauth['provider'], omniauth['uid'])
if authentication
flash[:notice] = "Signed in successfully."
sign_in_and_redirect(:user, authentication.user)
elsif current_user
current_user.authentications.create(:provider => omniauth['provider'], :uid => omniauth['uid'])
flash[:notice] = "Authentication successful."
redirect_to authentications_url
else
user = User.new
user.apply_omniauth(omniauth)
if user.save
flash[:notice] = "Signed in successfully."
sign_in_and_redirect(:user, user)
else
session[:omniauth] = omniauth.except('extra')
redirect_to new_user_registration_url
end
end
end

def destroy
@authentication = current_user.authentications.find(params[:id])
@authentication.destroy
flash[:notice] = "Successfully destroyed authentication."
redirect_to authentications_url
end

end

RegistrationsController: class RegistrationsController < Devise::RegistrationsController

def create
super
session[:omniauth] = nil unless @user.new_record? 
end

private
def build_resource(*args)
super
if session[:omniauth]
@user.apply_omniauth(session[:omniauth])
@user.valid?
end
end

end

User Model: class User < ActiveRecord::Base # associations has_many :authentications

# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, 
:validatable, :email_regexp =>  /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i

# Setup accessible (or protected) attributes for your model
attr_accessible :firstname, :lastname, :email, :password, :password_confirmation, :remember_me

# validations
validates :firstname, :presence => true
validates :lastname, :presence => true

validates :email, :presence => true, :uniqueness => true
validates_email :email

validates :password, :presence => true, :confirmation =>true            

# omniauth reference 3rd party
def apply_omniauth(omniauth)
if self.firstname.blank?
self.firstname = omniauth['info']['first_name'].presence || omniauth['info']['name'].presence || " "
end

if self.lastname.blank?
self.lastname = omniauth['info']['last_name'].presence || omniauth['info']['name'].presence || " "
end

if self.email.blank?
self.email = omniauth['info']['email'].presence
end

authentications.build(:provider => omniauth['provider'], :uid => omniauth['uid'])
end

# omit validation for omniauth session
def password_required?
(authentications.empty? || !password.blank?) && super
end

end

Authentication Model class Authentication < ActiveRecord::Base # associations belongs_to :user

# Setup accessible (or protected) attributes for your model  
attr_accessible :user_id, :provider, :uid
end

While debugging I found out that the 'authentications.build()' in the 'apply_omniauth(omniauth)' method produces an empty object, so that the 'password_required?' is always be true and a password must be provided.

ADDITIONAL QUESTION: why does 'authentications.empty?' always return 'false'?

Thanks in advance

Jan

1
This question is in serious need of editing.Jonathan Allard

1 Answers

2
votes

First of all, the password_required? method should be defined like this:

def password_required?
  (authentications.empty? || !password.blank?) && super
end

Then, in your view, you should wrap your password fields like this:

# app/views/registrations/new.html.erb
<% if @user.password_required? %>
  <%= f.label :password %>
  <%= f.password_field :password %>
  <%= f.label :password_confirmation %>
  <%= f.password_field :password_confirmation %>
<% else %>
  ...
<% end %>

Then in your controller or model, when creating an authentication method for the first time, you should use build and not create or new because that way authentications.empty? will return true and you'll be asked to enter a password.

Finally, as for validations, you should take a look at the client_side_validations' wiki about custom validations. You'll probably need to override the password validation.