Подтвердить что ты не робот

Аутентифицировать пользователя с помощью omniauth и Facebook для API-интерфейсов rails?

Я создаю Rails API и успешно создал способ для аутентификации пользователя с использованием Omniauth Identity.

Мы просто отправляем сообщение auth/identity/callback от клиента, передавая auth_key и пароль.
Затем сервер возвращает маркер doorkeeper, который затем использует пользователи, чтобы получить доступ к приложению и идентифицировать себя.

Эта диаграмма иллюстрирует это:

Client server relationship

Теперь мы хотели бы реализовать логин Facebook от клиента, но у него проблемы с его работой, как теоретически, так и практически.

В простом Rails-приложении с идентификатором Omniauth вы просто вызываете auth/facebook, но если мы поместим ссылку с этого в клиенте, он вызовет сервер и сервер, а затем запишет:

INFO -- omniauth: (facebook) Request phase initiated.

Приложение настроено правильно в Facebook с идентификатором и секретным, поэтому, возможно, запрос на вход в систему возвращается на сервер?

Я запутываюсь, но привязываю аутентификацию. Любая помощь с благодарностью оценили!

enter image description here

4b9b3361

Ответ 1

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

Я обсужу решение для Facebook, как это было в моем случае, но все может обратиться к любому другому провайдеру.

сначала вы должны знать, как работает omniauth2 (как описано для людей здесь)...

  1. Клиент: открыть всплывающее окно для аутентификации пользователя.
  2. Клиент: Войдите (при необходимости), затем авторизуйте приложение.
  3. Клиент: после успешного авторизации всплывающее окно перенаправляется обратно в ваше приложение. с code строки запроса ( code авторизации)

обратный URL-адрес обратной связи должен соответствовать вашему URL-адресу внешнего интерфейса, а не второму URL-адресу, и он должен быть указан в ваших конфигурациях приложений facebook

  1. Клиент: параметр code отправляется обратно в родительское окно, открывающее всплывающее окно.
  2. Клиент. Окно родителя закрывает всплывающее окно и отправляет запрос POST для backend/auth/facebook с параметром code.
  3. Сервер: code (code авторизации) обменивается на access token

здесь подробно описано, как обменивать code для access-token из документации разработчиков facebook

  1. Сервер: используйте access-token полученный на шаге 6, чтобы получить информацию о пользователе. как описано здесь с красивыми наглядными пособиями.

  2. VOILA у вас есть пользователь, который вы можете объединить/создать учетную запись для /link с другими поставщиками oauth/etc. но в виду, что пользователь может отменить некоторые разрешения (например, электронная почта, facebook поддерживает отмену некоторых разрешений)...


(достаточно говорить, показать мне код)

Прежде всего, вы должны добавить HTTParty gem в свой Gemfile

gem 'httparty'  # Makes http fun again (http client)

Я добавил этот gist, который содержит поток для шага (6, 7 и 8), которые являются наиболее проблематичными шагами и практически не документированы.

сущность экспорта 2 основных метода:

Omniauth::Facebook.authenticate(authorization_code)

который используется для аутентификации пользователя с помощью facebook и возврата user_info, long_live_access_token (действителен в течение 60 дней)

Omniauth::Facebook.deauthorize(access_token)

который используется для отмены авторизации/аннулирования прав доступа и прав приложения на facebook...

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

вот как он используется в контроллере

user_info, access_token = Omniauth::Facebook.authenticate(params['code'])
if user_info['email'].blank?
  Omniauth::Facebook.deauthorize(access_token)
end

Что это... теперь, если вас интересуют внутренности реализации... вот код, как видно из сути. (добавлено для справки) Не стесняйтесь разветвлять его, редактировать, помогать сделать его лучше.

require 'httparty'

module Omniauth
  class Facebook
    include HTTParty

    # The base uri for facebook graph API
    base_uri 'https://graph.facebook.com/v2.3'

    # Used to authenticate app with facebook user
    # Usage
    #   Omniauth::Facebook.authenticate('authorization_code')
    # Flow
    #   Retrieve access_token from authorization_code
    #   Retrieve User_Info hash from access_token
    def self.authenticate(code)
      provider = self.new
      access_token = provider.get_access_token(code)
      user_info    = provider.get_user_profile(access_token)
      return user_info, access_token
    end

    # Used to revoke the application permissions and login if a user
    # revoked some of the mandatory permissions required by the application
    # like the email
    # Usage
    #    Omniauth::Facebook.deauthorize(access_token)
    # Flow
    #   Send DELETE /me/permissions?access_token=XXX
    def self.deauthorize(access_token)
      options  = { query: { access_token: access_token } }
      response = self.delete('/me/permissions', options)

      # Something went wrong most propably beacuse of the connection.
      unless response.success?
        Rails.logger.error 'Omniauth::Facebook.deauthorize Failed'
        fail Omniauth::ResponseError, 'errors.auth.facebook.deauthorization'
      end
      response.parsed_response
    end

    def get_access_token(code)
      response = self.class.get('/oauth/access_token', query(code))

      # Something went wrong either wrong configuration or connection
      unless response.success?
        Rails.logger.error 'Omniauth::Facebook.get_access_token Failed'
        fail Omniauth::ResponseError, 'errors.auth.facebook.access_token'
      end
      response.parsed_response['access_token']
    end

    def get_user_profile(access_token)
      options = { query: { access_token: access_token } }
      response = self.class.get('/me', options)

      # Something went wrong most propably beacuse of the connection.
      unless response.success?
        Rails.logger.error 'Omniauth::Facebook.get_user_profile Failed'
        fail Omniauth::ResponseError, 'errors.auth.facebook.user_profile'
      end
      response.parsed_response
    end


    private

    # access_token required params
    # https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/v2.3#confirm
    def query(code)
      {
        query: {
          # The authorization_code we want to exchange for the access_token
          code: code,
          # This must match the redirectUrl registerd in the facebook app.
          # You can save it to ENV['WEB_APP_URL'] if you have multiple facebook apps for development and testing
          # so you can support testing app on development and production app on production env.
          redirect_uri: "http://localhost:9000/",
          client_id: ENV['FB_APP_ID'], # Facebook appId
          client_secret: ENV['FB_APP_SECRET'], # Facebook app secret (must not exist on front-end app for security)
        }
      }
    end
  end
end

вот еще один учебник nodejs, реализующий oauth для instagram, который помог мне понять, как работает oauth2 (добавлено для справки)

Ответ 2

Чтобы решить эту проблему, лучшим ресурсом, который я нашел, является пример приложения rails в сателлитете github repo: https://github.com/sahat/satellizer/tree/master/examples/server/ruby

Ваш код сателлитатора вызывает метод AuthController.authenticate. Этот метод использует классы oauth для каждого поставщика для преобразования кода, который вы получаете в токен доступа. Затем в своем классе пользователя вы можете получить пользователя, который соответствует информации, полученной вами у поставщика.

В конце метод контроллера возвращает токен jwt клиенту.

В моем случае часть контроллера немного отличается, потому что я также использую программу для проверки подлинности по почте/паролю, но я копирую классы oauth как есть, и это работает как прелесть.