【问题标题】:How to use Omniauth Asana with Rails API only app如何将 Omniauth Asana 与仅限 Rails API 的应用程序一起使用
【发布时间】:2017-05-31 09:13:28
【问题描述】:

我有一个仅限 Rails 5 API 的应用程序和一个 Angular JS 前端应用程序,并希望与 Asana API 集成。我正在使用 ruby-asanaomniauthomniauth-asana 宝石。

我使用 Asana 的 JS library 开始请求,如下所示:

var client = Asana.Client.create({
      clientId: 172706773623703,
      clientSecret: '<client_secret>',
      redirectUri: '<redirect_url>'
    });
    client.useOauth({
      flowType: Asana.auth.PopFlow
    });

上面确实将我重定向到我可以登录的 Asana。在redirectUri 上,我提供了一个后端路由(仅限Rails 5 API),该路由应该处理身份验证的剩余部分(仅使用JS 我只得到一个不能自我更新的临时令牌,这意味着用户必须对每个令牌过期的时间。如果我正确理解了文档)。

因此,在我为处理路由而创建的控制器上,我有以下内容(来自 Asana 文档中的示例):

require 'omniauth-asana'
use OmniAuth::Strategies::Asana, <secret>, <secret>

creds = request.env["omniauth.auth"]["credentials"].tap { |h| h.delete('expires') }
strategy = request.env["omniauth.strategy"]
access_token = OAuth2::AccessToken.from_hash(strategy.client, creds).refresh!
$client = Asana::Client.new do |c|
  c.authentication :oauth2, access_token
end

现在,上述方法不起作用,因为 1) 没有 request.env,因为这是一个仅限 API 的应用程序,所以我按照 Omniauth 上的说明进行操作,并将以下内容添加到我的 config/application.rb

config.session_store :cookie_store, key: '_interslice_session'
config.middleware.use ActionDispatch::Cookies # Required for all session management
config.middleware.use ActionDispatch::Session::CookieStore, config.session_options

现在,在request.headers 我有_interslice_session,其中有一些数字。如何使用以上内容创建Asana client

有什么想法吗?

【问题讨论】:

    标签: ruby-on-rails ruby api asana


    【解决方案1】:

    好的,我想我明白你在这里尝试做什么了;我认为最好的方法是从 OAuth 的授权代码授予的一般情况开始,然后进入 OmniAuth 的细节。

    • 您将用户发送到 Asana 拥有的 URL;也就是说,您的目标是让用户访问特定的 url。对于 Asana,这是https://app.asana.com/-/oauth_authorize。 (请注意,如果您未发送正确的 client_id 参数,我们会以错误响应,但如果您愿意,请随时检查该链接)。在此请求期间不要发送client_secret - 它永远不会涉及客户端代码,因为这是不安全的。

    • 如果他们同意授予访问权限,Asana 会向用户的浏览器发送一个重定向请求,其中包含一个短期代码。这意味着将使用此代码作为参数从用户的浏览器调用您的服务器,因此必须处理来自浏览器的 new 传入请求到您指定为重定向 URI 的任何内容。此外,您的集成的所有用户无论身在何处都必须可以访问此位置。

    • 您将此代码作为 POST 请求从您的服务器发送到 https://app.asana.com/-/oauth_token,并将您的 client_secret 发送到 Asana 以获取刷新令牌。这是您的应用程序实际要求提供凭据的地方;前面阶段给出的令牌只是简单地承认,在短时间内,用户已授予您的应用程序请求这些凭据的权限,并且您的 client_secret 向 Asana 保证,对于此服务器端请求,您的应用程序确实是您的(这就像您的应用程序的密码)。

    • 我们发回一个access_token,它代表(大约)一个有效期为一小时的客户端-用户凭证对。 您使用这些凭证代表该用户访问我们的 API。我们还发回一个长期存在的refresh_token,用于在它们过期后以非常相似的方式获取新的短期access_tokens

    好的,如果我正确理解它,它与 OmniAuth 的工作原理是它希望处理几乎所有的事情。我将在我们的ruby-asana 客户端库中处理我们的omniauth 示例:https://github.com/Asana/ruby-asana/blob/master/examples/omniauth_integration.rb

    1. 您使用您的客户端 ID 和客户端密码设置 OmniAuth

      use OmniAuth::Strategies::Asana, <client_id>, <client_secret>
      
    2. 收到了一个请求,但您没有它的凭据。

      get '/' do
        if $client
          ...
        else
          '<a href="/sign_in">sign in to asana</a>'
        end
      end
      
    3. 用户单击登录链接,该链接(代码省略)将其发送到sign_in 端点。此端点发出重定向到/auth/asana

    4. 浏览器从我们的服务器请求/auth/asana。如果你看那个例子,它并没有在我们的代码中实现。那是因为/auth/:provider 由 OmniAuth 神奇地处理。

    5. 这就是所有魔法发生的地方。 OmniAuth 处理上面的整个登录流程:将浏览器发送到我们上面的oauth_authorize,然后接收回调并将相关参数粘贴在环境中,这样它就知道“我们刚刚得到了短暂的代码”。当这些线被击中时:

      creds = request.env["omniauth.auth"]["credentials"].tap { |h| h.delete('expires') }
      strategy = request.env["omniauth.strategy"]
      

    您处于 OmniAuth 拦截的回调中,获取了所需的凭据,并在环境中设置了凭据。您不必手动处理 oauth 回调和令牌交换。


    现在,根据您提供的代码,我马上注意到了一些事情:

    1. 您导致弹出周期发生在客户端。可能(我强烈怀疑)这不适用于 OmniAuth - 它需要处理整个 OAuth 流程。
    2. 根据您提供的代码 sn-p,您不是在控制器中的请求-响应周期之外提供此服务,而是在控制器主体中而不是在实例方法中。这可能是一个错字,但这需要在 Rails 外部回调的方法中(也就是说,路由必须指向 Asana 可以用来处理浏览器请求的控制器方法)。
    3. 我认为您不必查看request.headers - 我不确定request.env 可能存在什么问题,但我怀疑它们可能与您的应用程序的仅API 特性无关。你确定这是因为它只是 API 吗?添加中间件后,您是否仔细检查了您无法访问request.env?我的直觉是request.env 中的持久数据仍然存在,只有 it 需要添加中间件来执行此操作。 OmniAuth 上的说明只是说你需要为你的 API 建立一个会话存储——这对我来说很有意义,因为 API 不一定需要跨请求存储状态,OmniAuth 告诉你把一个会话存储放回去。

    我知道这是很多信息,但希望它可以帮助您走上正轨。干杯!

    【讨论】:

    • 谢谢马特!您非常详细的回复为我提供了使身份验证正常工作所需的所有信息。非常感谢!
    猜你喜欢
    • 2022-06-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-21
    相关资源
    最近更新 更多