为 Rails Api-only 应用实施社交登录

Implementing social login for Rails Api-only app

我正在努力寻找一种工作方法来通过 Facebook 和 Google 为我现有的 api-only rails 应用程序实现 oauth2 登录。登录流程和 jwt 管理是使用 Devise 和 Doorkeeper 完成的,遵循 this guide.

我尝试了 Doorkeeper-grants-assertion 个示例,但其中 none 个示例有效。 我遇到的问题是我无法用我的 jwt 令牌交换提供商的令牌。

客户端(Android 和 iOS 应用程序)我可以使用提供商登录并获取令牌,但是当我尝试授权用户创建新令牌时,它给我错误。

代码同示例。在 Google 的情况下,我将跳过令牌请求,因为我已经可以从客户端获取它:

class GoogleController

  def initialize(auth_code)
    @auth_code = auth_code
    @user_data = user_data
  end

  def user_data
    url = "https://www.googleapis.com/oauth2/v3/userinfo?access_token=" + @auth_code
    response = Faraday.get(url, {:Accept => 'application/json'})
    @resp = JSON.parse(response.body)
  end

  def email
    @resp['email']
  end

  def first_name
    @resp['first_name']
  end

  def last_name
    @resp['last_name']
  end

  def get_user!
    # below you should implement the logic to find/create a user in your app basing on @user_data
    # It should return a user object
    user = User.find_by(email: email)
    if user
      Rails.logger.info "User"
      user
    else
      user = User.new(email: email, password: Devise.friendly_token.first(10))
      user.save
      Rails.logger.info "No User"
      user
    end
  end
end

我正在使用邮递员发出请求,如果我的 body 是:

,下面是响应
{
"client_id": "doorkeeper_app_uid",
"client_secret": "doorkeeper_app_secret",
"grant_type": "assertion",
"provider": "google",
"assertion": "MY USER TOKEN" }

{ "error": "invalid_client",
"error_description": "Client authentication failed due to unknown client, no client authentication included, or unsupported authentication method." }

我刚刚发现我没有 return 用户 object,这就是 Facebook 无法正常工作的原因。 现在,使用相同的代码,只有不同的令牌端点,Facebook 登录正在工作,我可以找到或创建用户和 return jwt 令牌,而 Google 不是。

如果有人能指出正确的方向,那就太好了。

编辑

经过进一步调查,我在这一点上: 我可以找到或创建我的 Google 经过身份验证的用户,但是当我 return 它向门卫断言授权扩展时,它无法验证

def validate_resource_owner
  !resource_owner.nil?
end

在class

PasswordAccessTokenRequest

而且我无法生成新的 jwt 令牌。 与使此验证失败的 facebook 有何不同?

不可思议的家伙,神秘的事情发生了,但我找到了解决办法。

如果这样写,doorkeeper.rb 初始化程序中的 2 个提供程序不知何故发生了冲突:(不要这样做)

resource_owner_from_assertion do
  if provider == "facebook"
    g = Api::V1::FacebookController.new(params[:assertion])
    g.get_user!
  end
  if provider == "google"
    g = Api::V1::GoogleController.new(params[:assertion])
    return g.get_user!
  end
end

而是做类似的事情:

resource_owner_from_assertion do
  provider = params[:provider]
  controller = (provider == "facebook") ? Api::V1::FacebookController.new(params[:assertion]) : Api::V1::GoogleController.new(params[:assertion])
  controller.get_user!
end

然后控制器内部还有另一个问题,因为我使用“user”作为变量名:

user = User.find_by(email: email)

这显然很糟糕,所以使用

facebook_user = User.find_by(email: email)

现在一切似乎都按预期进行。我希望有人会觉得这很有用。