首页 文章

使用React Frontend Rails API对Linkedin OAuth进行身份验证

提问于
浏览
0

到目前为止,我的网站一直在使用Rails直接生成的fullstack视图,对于linkedin身份验证,我使用redirect_uri路由到 Devise::OmniauthCallbacksController 来处理逻辑 .

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

我想知道如何将其迁移到一个独立的Ract前端,一个独立的Rails服务器 . React应用程序应“生成”一个用于授权的LUN弹出窗口,并等待所有授权过程完成后再关闭自身并继续在主窗口上进行注册 .

我已经通过Linkedin OAuth guide,我看到从批准连接到获取用户信息的方式很长 . 我很高兴Devise / Omniauth为我服务器端完成大部分工作,我宁愿保持这种方式,而不是在前端编写逻辑 .

(顺便说一句,假设Linkedin OAuth流程类似于the Google one mentionned in this answer是否安全?)

我不确定这是否可以按照我在React应用程序上看到的方式完成(特别是在linkedin弹出窗口和主反应视图之间的数据通信) . 另外根据我的理解,rails中的 omniauth gem 期望从OAuth提供者接收一次性令牌,然后才能再次查询提供者两次(第一次交换访问令牌,第二次获取用户信息) . 以下工作会怎样?

  • 假设我的React Client应用程序托管于 client.example.com

  • 假设我的服务器api托管于 api.example.com

  • React应用程序打开一个弹出窗口,用于转到Linkedin auth URI

  • (1)重定向uri,它将直接指向api.example.com

  • (2)client.example.com上的redirect_uri,然后我的React客户端必须将令牌转发到api.example.com

  • 授权码有点到达我的服务器 api.example.com ,它获取访问代码并从Linkedin检索用户数据 . 它返回浏览器 identity_id ,可用于注册

  • identity_id 实际上是返回到弹出窗口,并转移回主React应用程序(如何?)

1 回答

  • 2

    我有同样的问题,我发现了一个hacky但有趣的解决方案:-)
    (我使用 deviseomniauth-linkedin gems中的所有OAuth逻辑 . )

    在我的应用程序中,您可以开始填写表单,然后在保存之前需要登录 . 我需要一种方法来记录用户而不会丢失表单数据而无需重新加载页面 .

    当用户访问该页面时,他将获得一个唯一的uuid来识别他 . 这个uuid用于启动websocket连接,因此我可以稍后将数据直接推送到此选项卡 .

    我使用javascript window.open() 在新标签页中打开LinkedIn OAuth,因此我可以使用 window.close() 关闭此标签 .

    您可以将其他参数传递给OAuth身份验证,我传递唯一的uuid,以便在身份验证成功时我可以恢复它并使用此uuid我可以通过websocket发送消息通知第一个选项卡(在我的应用程序中我发送用户infos用当前用户更新状态) .

    身份验证后,我将用户重定向到仅包含 window.close() 的页面 .

    enter image description here

    在Rails中设置LinkedIn OAuth

    您需要一个功能性的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登录/创建用户 .

    Websocket连接

    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(有设计和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>
    

    希望这有帮助:)

相关问题