Devise/Omniauth — как работать с провайдером, который не включает электронную почту

У меня есть приложение Rails, которое использует Devise и Omniauth для обработки аутентификации. Как предписано, я получаю обратные вызовы от провайдеров в моем omniauth_callbacks_controller, где я проверяю, существует ли уже аутентификация и существует ли уже пользователь с адресом электронной почты, предоставленным провайдером, при необходимости создавая нового пользователя.

Мне нужен действующий адрес электронной почты для каждого пользователя. Моя проблема связана с обратными вызовами из Twitter. Твиттер не предоставляет адрес электронной почты для своих пользователей, поэтому я не могу создать действительного пользователя. Чтобы решить эту проблему, я сохраняю данные провайдера в сеансе и отправляю пользователю новую страницу регистрации, прося его указать свой адрес электронной почты, чтобы я мог создать действительного пользователя. Когда эта форма отправлена, я столкнулся с проблемой. Форма создала нового пользователя, но вполне возможно, что пользователь с таким адресом электронной почты уже существует (в этом случае мне нужно будет добавить аутентификацию для этого пользователя).

На данный момент я проверяю, существует ли пользователь с той же электронной почтой, что и новый пользователь. Если это так, я игнорирую нового пользователя и применяю аутентификацию к уже существующему пользователю. Однако это кажется действительно хакерским.

Как я должен это делать?

class Users::RegistrationsController < Devise::RegistrationsController

def build_resource(*args)
    super
    if session[:omniauth]
      #If a user with this email already exists then use them instead
      existing_user = User.find_by_email(@user.email)
      if(existing_user)
          existing_user.email = @user.email
          @user = existing_user
      end
      #If there is a session available, we know it contains data for adding an authentication
      @user.apply_omniauth_data_as_authentication(session[:omniauth])
      #Ensure validations are passed on to next page
      @user.valid?
    end
  end

person Undistraction    schedule 20.02.2012    source источник
comment
вам удалось это исправить? У меня такая же проблема.   -  person Finnnn    schedule 29.04.2012
comment
@Finnnn Только что опубликовал свой контроллер Omniauth Callbacks в ответе.   -  person Undistraction    schedule 29.04.2012


Ответы (2)


Ну вот. Я постараюсь вспомнить, откуда я взял исправление, и опубликую ссылку, если найду:

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController

  # This controller handles callbacks from Omniauth providers. If a user already exists with this provider we sign them in.
  # Otherwise we either add an authentication to the current user, or create a new user if one doesn't exist.

  def facebook
    authorize
  end

  def twitter
    authorize
  end

  private

    def authorize
      omniauth_data = request.env["omniauth.auth"]
      #Check to see if we have an authentication for this provider already stored for any User
      authentication = Authentication.find_by_provider_and_uid(omniauth_data['provider'], omniauth_data['uid'])
      #If an authentication already exists, sign the owning User in
      if authentication
        flash[:notice] = "Signed in successfully with " +  omniauth_data['provider'].titleize
        sign_in_and_redirect(:user, authentication.user)
      #Otherwise if there is a current User, add a new authentication for them
      elsif current_user
        current_user.authentications.create(:provider => omniauth_data['provider'], :uid => omniauth_data['uid'], :token => omniauth_data['token'])
        flash[:notice] = "Authentication successful"
        redirect_to user_profile_url
      #Otherwise we check if a user exists with the email address provided by the provider (if provided at all))
      else
        email = omniauth_data['info']['email']
        user = User.find_by_email(email)
        #If no user exists, create a new one
        if(!email || !user)
            user = User.new
        end
        user.apply_omniauth_data_as_authentication(omniauth_data)
        #If they save successfully (meaning we have enough data from the authorisation) sign them in
        if user.email?
          #We got all we needed from the provider so we don't need them to confirm'
          user.skip_confirmation!
          user.save!
          flash[:notice] = "Signed in successfully with " +  omniauth_data['provider']
          sign_in_and_redirect(:user, user)
        #Otherwise we need to have the visitor manually submit missing information
        else
          #save the omniauth data in a session so we can add the authentication once registration is complete
          flash[:alert] = "Please complete registration"
          session[:omniauth] = omniauth_data.except('extra')
          redirect_to new_user_registration_url
        end
      end
    end

end
person Undistraction    schedule 29.04.2012

Мне нравится решение, используемое по этой ссылке http://www.orhancanceylan.com/rails-twitter-and-facebook-authentications-with-omniauth-and-devise/

Он использует учетные данные провайдера для регистрации в обычном процессе разработки (а также для заполнения атрибутов провайдера и uid пользователя или отдельной таблицы). Таким образом, когда новый пользователь пытается зарегистрироваться с помощью провайдера, ему потребуется ввести адрес электронной почты и пароль, что создает меньше головной боли в будущем (например, желание войти в систему обычным способом, но не имея пароля для ввода, и тогда вам придется справиться с этим).

Хотя это решение несколько сводит на нет удобство регистрации в социальных сетях ПЕРВЫЙ раз, оно создает долгосрочные положительные эффекты с минимальными усилиями.

Если вы абсолютно не хотите такого рода требований из-за боязни потерять подписки или сделать ваш веб-сайт менее совершенным, найдите другое решение и сделайте всю работу, чтобы справиться с этими последующими случаями.

person ahnbizcad    schedule 02.10.2014