如何将 Omniauth Asana 与 Rails API 应用程序一起使用
How to use Omniauth Asana with Rails API only app
我有一个 Rails 5 API 应用程序和一个 Angular JS 前端应用程序,我想与 Asana API 集成。我正在使用 ruby-asana
、omniauth
和 omniauth-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
?
有什么想法吗?
好的,我想我明白你在这里试图做什么;我认为最好的方法是从 OAuth 的授权代码授予的一般情况开始,然后进入 OmniAuth 的细节。
您将用户转到 Asana 拥有的 URL;也就是说,您的目标是让用户访问特定的 url。对于 Asana,这是 https://app.asana.com/-/oauth_authorize。 (请注意,如果您没有发送正确的 client_id
参数,我们会以错误响应,但如果需要,请随时检查 link)。在此请求期间不要发送client_secret
- 它永远不会涉及客户端代码,因为这是不安全的。
如果他们同意授予访问权限,Asana 会向用户的浏览器发送一个重定向请求,并附上一个短期代码。这意味着您的服务器将使用此代码作为参数从用户的浏览器调用,因此必须处理从浏览器到您指定为重定向 URI 的任何内容的 new 传入请求。此外,您的集成的所有用户无论身在何处都必须可以访问此位置。
您将此代码作为 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
您使用客户端 ID 和客户端密码设置了 OmniAuth
use OmniAuth::Strategies::Asana, <client_id>, <client_secret>
收到一个请求,但您没有它的凭据。
get '/' do
if $client
...
else
'<a href="/sign_in">sign in to asana</a>'
end
end
用户单击 link 中的登录,这(省略代码)将他们发送到 sign_in
端点。此端点发出重定向到 /auth/asana
浏览器向我们的服务器请求/auth/asana
。如果你看那个例子,它没有在我们的代码中实现。那是因为 OmniAuth 神奇地处理了 /auth/:provider
。
这就是所有魔法发生的地方。 OmniAuth 处理上面的整个登录流程:将浏览器发送到我们上面的 oauth_authorize
,然后接收回调并将相关参数粘贴到环境中,以便它知道 "we just got the short lived code"。当这些行被命中时:
creds = request.env["omniauth.auth"]["credentials"].tap { |h| h.delete('expires') }
strategy = request.env["omniauth.strategy"]
您在 OmniAuth 拦截的回调中,获得了所需的凭据,并在环境中设置了凭据。您不必手动处理 oauth 回调和令牌交换。
现在,根据您提供的代码,我立即注意到一些事情:
- 您正在导致弹出循环发生在客户端。可能(我强烈怀疑)这不适用于 OmniAuth - 它期望处理整个 OAuth 流程。
- 根据您提供的代码片段,您并不是在控制器中的请求-响应周期之外提供它,相反,它似乎是在控制器主体中而不是在实例方法之外。这可能是一个拼写错误,但这需要在一个回调到 Rails 外部的方法中(也就是说,一个路由必须指向这个 Asana 可以用来处理浏览器请求的控制器方法)。
- 你不必看
request.headers
,我想 - 我不确定 request.env
可能有什么问题,但我怀疑它们可能与 API-您的应用程序的唯一性质。您确定这是因为它仅 API 吗?添加中间件后,您是否仔细检查无法访问request.env
?我的预感是 request.env
中的持久数据仍然存在,只有 it 需要添加中间件才能执行此操作。 OmniAuth 上的说明只是说你需要为你的 API 设置一个会话存储——这对我来说很有意义,因为 APIs 不一定需要跨请求存储状态,OmniAuth 告诉你将会话存储放回原处。
我知道这是很多信息,但希望它能帮助您走上正轨。干杯!
我有一个 Rails 5 API 应用程序和一个 Angular JS 前端应用程序,我想与 Asana API 集成。我正在使用 ruby-asana
、omniauth
和 omniauth-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
?
有什么想法吗?
好的,我想我明白你在这里试图做什么;我认为最好的方法是从 OAuth 的授权代码授予的一般情况开始,然后进入 OmniAuth 的细节。
您将用户转到 Asana 拥有的 URL;也就是说,您的目标是让用户访问特定的 url。对于 Asana,这是 https://app.asana.com/-/oauth_authorize。 (请注意,如果您没有发送正确的
client_id
参数,我们会以错误响应,但如果需要,请随时检查 link)。在此请求期间不要发送client_secret
- 它永远不会涉及客户端代码,因为这是不安全的。如果他们同意授予访问权限,Asana 会向用户的浏览器发送一个重定向请求,并附上一个短期代码。这意味着您的服务器将使用此代码作为参数从用户的浏览器调用,因此必须处理从浏览器到您指定为重定向 URI 的任何内容的 new 传入请求。此外,您的集成的所有用户无论身在何处都必须可以访问此位置。
您将此代码作为 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
您使用客户端 ID 和客户端密码设置了 OmniAuth
use OmniAuth::Strategies::Asana, <client_id>, <client_secret>
收到一个请求,但您没有它的凭据。
get '/' do if $client ... else '<a href="/sign_in">sign in to asana</a>' end end
用户单击 link 中的登录,这(省略代码)将他们发送到
sign_in
端点。此端点发出重定向到/auth/asana
浏览器向我们的服务器请求
/auth/asana
。如果你看那个例子,它没有在我们的代码中实现。那是因为 OmniAuth 神奇地处理了/auth/:provider
。这就是所有魔法发生的地方。 OmniAuth 处理上面的整个登录流程:将浏览器发送到我们上面的
oauth_authorize
,然后接收回调并将相关参数粘贴到环境中,以便它知道 "we just got the short lived code"。当这些行被命中时:creds = request.env["omniauth.auth"]["credentials"].tap { |h| h.delete('expires') } strategy = request.env["omniauth.strategy"]
您在 OmniAuth 拦截的回调中,获得了所需的凭据,并在环境中设置了凭据。您不必手动处理 oauth 回调和令牌交换。
现在,根据您提供的代码,我立即注意到一些事情:
- 您正在导致弹出循环发生在客户端。可能(我强烈怀疑)这不适用于 OmniAuth - 它期望处理整个 OAuth 流程。
- 根据您提供的代码片段,您并不是在控制器中的请求-响应周期之外提供它,相反,它似乎是在控制器主体中而不是在实例方法之外。这可能是一个拼写错误,但这需要在一个回调到 Rails 外部的方法中(也就是说,一个路由必须指向这个 Asana 可以用来处理浏览器请求的控制器方法)。
- 你不必看
request.headers
,我想 - 我不确定request.env
可能有什么问题,但我怀疑它们可能与 API-您的应用程序的唯一性质。您确定这是因为它仅 API 吗?添加中间件后,您是否仔细检查无法访问request.env
?我的预感是request.env
中的持久数据仍然存在,只有 it 需要添加中间件才能执行此操作。 OmniAuth 上的说明只是说你需要为你的 API 设置一个会话存储——这对我来说很有意义,因为 APIs 不一定需要跨请求存储状态,OmniAuth 告诉你将会话存储放回原处。
我知道这是很多信息,但希望它能帮助您走上正轨。干杯!