Linkedin OAuth-аутентификация с помощью React Frontend + Rails API

До сих пор мой веб-сайт использовал представления полного стека, сгенерированные непосредственно Rails, и для проверки подлинности с помощью я использовал redirect_uri, перенаправленный на Devise::OmniauthCallbacksController обращаться с логикой.

GET https://www.linkedin.com/oauth/v2/authorization?redirect_uri=my_devise_omniauth_callback_url

Мне интересно, как перенести это на автономный интерфейс React + автономный сервер Rails. Приложение React должно "вызвать" всплывающее окно для авторизации и дождаться завершения процесса авторизации, прежде чем закрыться и продолжить регистрацию в главном окне.

Я прошел через руководство по Linkedin OAuth, и я вижу, что путь от одобрения соединения до получения информации о пользователе довольно длинный. Я рад, что Devise/Omniauth выполняет большую часть работы для меня на стороне сервера, и я бы предпочел оставить это так, вместо того, чтобы кодировать эту логику во внешнем интерфейсе.

(Между прочим, безопасно ли предположить, что поток OAuth Linkedin похож на тот, который Google упоминал в этом ответе?)

Я не уверен, можно ли это сделать так, как я это вижу в приложении React (особенно обмен данными между всплывающим окном linkedin и основным видом реакции). Также из того, что я понимаю omniauth gem Ожидается, что в rails получит одноразовый токен от поставщика OAuth, прежде чем он сможет запросить поставщика еще дважды (первый раз для обмена токеном доступа, второй раз для получения информации о пользователе). Будет ли следующая работа?

  • Предположим, что мое приложение React Client размещено на client.example.com
  • Предположим, мой сервер API находится на api.example.com
  • Приложение React открывает всплывающие окна, которые используются для перехода на URI Linkedin Auth с
    • (1) URI перенаправления, который будет указывать непосредственно на api.example.com
    • (2) Redirect_uri на client.example.com, и мой клиент React должен будет перенаправить токен на api.example.com.
  • Код авторизации доходит до моего сервера api.example.com, который получает код доступа и извлекает данные пользователя из Linkedin. Возвращает браузеру identity_id который можно использовать для регистрации
  • identity_id фактически возвращается во всплывающее окно и возвращается в основное приложение React (как?)

1 ответ

У меня была такая же проблема, и я нашел хакерское, но интересное решение:-)
(Я использую всю логику OAuth из devise а также omniauth-linkedin драгоценные камни.)

В моем приложении вы можете начать заполнять форму, и вам необходимо войти в систему, прежде чем сохранить ее. Мне нужен был способ войти в систему пользователя без потери данных формы и без перезагрузки страницы.

Когда пользователь посещает страницу, ему предоставляется уникальный идентификатор для его идентификации. Этот uuid используется для запуска соединения через веб-сокет, поэтому позже я могу перенести данные прямо на эту вкладку.

Я открываю LinkedIn OAuth в новой вкладке с помощью JavaScript window.open() так что я могу закрыть эту вкладку с window.close(),

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

После аутентификации я перенаправляю пользователя на страницу, содержащую только window.close(),

Настройте LinkedIn OAuth в Rails

Вам понадобится функциональная проверка подлинности Omniauth/Devise.

omniauth_callback_controller.rb

class OmniauthCallbacksController < Devise::OmniauthCallbacksController

  def linkedin
    user = User.connect_to_linkedin(request.env["omniauth.auth"], current_user)
    guest_guid = request.env["omniauth.params"]["guestGuid"]

    if user.persisted?
      ActionCable.server.broadcast("guest:#{guest_guid}", { user: UserSerializer.new(user).to_h })
      sign_in(user)
      redirect_to landing_home_index_path
    end   
  end
end

user.rb

def self.connect_to_linkedin(auth, signed_in_resource = nil)
  user = User.where(provider: auth.provider, linkedin_uid: auth.uid).first

  if user
    return user
  else
    registered_user = User.where(email: auth.info.email).first

    if registered_user
      return registered_user
    else
      user = User.create(lastname: auth.info.last_name, firstname: auth.info.first_name,
        provider: auth.provider, linkedin_uid: auth.uid, email: auth.info.email,
        linkedin_token: auth.credentials.token, linkedin_secret: auth.credentials.secret,
        linkedin_picture: auth.extra.raw_info.pictureUrl, password: Devise.friendly_token[0, 20])
    end
  end
end

routes.rb

  devise_for :users, controllers: {
    omniauth_callbacks: "omniauth_callbacks",
    sessions: "users/sessions"
  }

Теперь, если вы посетите /users/auth/linkedin Вы сможете войти / создать пользователя с LinkedIn OAuth.

Веб-сокет соединение

create_job_offer_channel.rb

class CreateJobOfferChannel < ApplicationCable::Channel
  def subscribed
    stream_from "guest:#{params[:guest]}"
  end

  def unsubscribed
  end

  def receive(data)
    ActionCable.server.broadcast("guest:#{params[:guest]}", data)
  end
end

create_job_offer.js.coffee

class window.CreateJobOffer
  constructor: (params) ->
    self = @

    @channel = App.cable.subscriptions.create {
      channel: "CreateJobOfferChannel", guest: params["guest"]
    }, self

    @onReceive = params["onReceive"] || null


  received: (data) =>
    @onReceive(data) if @onReceive

Реагировать на интерфейс

Ссылка на LinkedIn OAuth (с помощью devise и omniauth)

<div className="btn-linked-in">
  <i className="fa fa-linkedin-square"></i>
  <span onClick={() => open(`/users/auth/linkedin?guestGuid=${guestGuid}`)}>
    Login with LinkedIn
  </span>
</div>

Надеюсь это поможет:)

Другие вопросы по тегам