使用 google-auth-library-ruby 和 google-api-ruby-client 的三足 OAuth 和 one-time-code 流程
3-legged OAuth and one-time-code flow using google-auth-library-ruby with google-api-ruby-client
快速概览:我有一个 ruby 应用程序,每晚运行并使用用户的 google 日历做一些事情。用户已经通过单独的 React 应用授予访问权限。我无法让 ruby 应用程序使用 React 应用程序的授权代码访问用户的日历。
详细信息:我有一个 React front-end,它可以使用 gapi 登录用户,随后将用户登录到 Firebase。这是我配置 gapi obj 的方式:
this.auth2 = await loadAuth2WithProps({
apiKey: config.apiKey, // from firebase
clientId: config.clientId, // from gcp
// ....
access_type: "offline", // so we get get authorization code
})
这里是签到:
doSignInWithGoogle = async () => {
const googleUser = await this.auth2.signIn();
const token = googleUser.getAuthResponse().id_token;
const credential = app.auth.GoogleAuthProvider.credential(token);
return this.auth.signInWithCredential(credential);
};
用户的下一步是授予应用对其日历的离线访问权限:
doConnectGoogleCalendar = async () => {
const params = {scope:scopes};
const result = await this.auth2.grantOfflineAccess(params);
console.log(result.code); // logs: "4/ygFsjdK....."
};
此时前端拥有授权代码,可以将其传递给 server-side 应用程序以交换访问和刷新令牌。我一直无法找到使用用户提供的 auth-code 来调用可用范围的好方法。这就是我配置 oauth 客户端的方式:
auth_client = Google::APIClient::ClientSecrets.load(
File.join(Rails.root,'config','client_secrets.json') // downloaded from GCP
).to_authorization
^ 我在后端使用与前端相同的 GCP 凭据。它是一种 "OAuth 2.0 Client ID" 类型的凭证。 我不确定这是否是好的做法。另外,我是否需要定义与前端相同的配置(如 access_type 和范围)?
接下来我按照文档对 get the access and refresh tokens 的说明进行操作(单击 Ruby):
auth_client.code = authorization_code_from_frontend
auth_client.fetch_access_token!
---------
Signet::AuthorizationError (Authorization failed. Server message:)
{
"error": "invalid_grant",
"error_description": "Bad Request"
}
在设置单独的后端应用程序来处理对用户授权范围的离线访问时,我是否遗漏了什么?这些库中有很多不同的信息,但我无法将其提炼成有用的信息。
更新
我发现这个页面描述了我在其他任何地方都没有找到的“one-time-code flow”,这是我浏览过的所有文档。它确实回答了我上面的一个小问题:是的,您可以使用与后端 Web 应用程序相同的客户端机密。(请参阅底部的完整示例,他们就是这样做的).我将对其进行更多探索,看看是否可以解决我更大的问题。还将更新标题以包含 one-time-code 流程。
在对代码示例和源代码进行大量挖掘之后,我有了一个干净的工作解决方案。在 "update" 中找到该页面后,我发现 ClientSecrets
我做事的方式一直是 deprecated in favor of the google-auth-library-ruby 项目。我很高兴找到它,因为它似乎是一个更完整的解决方案,因为它为您处理所有令牌管理。这是设置所有内容的代码:
def authorizer
client_secrets_path = File.join(Rails.root,'config','client_secrets.json')
client_id = Google::Auth::ClientId.from_file(client_secrets_path)
scope = [Google::Apis::CalendarV3::AUTH_CALENDAR_READONLY]
redis = Redis.new(url: Rails.application.secrets.redis_url)
token_store = Google::Auth::Stores::RedisTokenStore.new(redis: redis)
Google::Auth::WebUserAuthorizer.new(client_id, scope, token_store, "postmessage")
end
然后这就是我如何使用授权码:
def exchange_for_token(user_id,auth_code)
credentials_opts = {user_id:user_id,code:auth_code}
credentials = authorizer.get_and_store_credentials_from_code(credentials_opts)
end
调用该方法后,库会将交换的令牌存储在 Redis 中(您可以配置存储位置)以备后用,如下所示:
def run_job(user_id)
credentials = authorizer.get_credentials(user_id)
service = Google::Apis::CalendarV3::CalendarService.new
service.authorization = credentials
calendar_list = service.list_calendar_lists.items
# ... do more things ...
end
信息太多,很难区分适用于每种情况的信息。希望这可以帮助其他受困于 "one-time-code flow" 的人,这样他们就不会花几天时间在办公桌上敲头。
快速概览:我有一个 ruby 应用程序,每晚运行并使用用户的 google 日历做一些事情。用户已经通过单独的 React 应用授予访问权限。我无法让 ruby 应用程序使用 React 应用程序的授权代码访问用户的日历。
详细信息:我有一个 React front-end,它可以使用 gapi 登录用户,随后将用户登录到 Firebase。这是我配置 gapi obj 的方式:
this.auth2 = await loadAuth2WithProps({
apiKey: config.apiKey, // from firebase
clientId: config.clientId, // from gcp
// ....
access_type: "offline", // so we get get authorization code
})
这里是签到:
doSignInWithGoogle = async () => {
const googleUser = await this.auth2.signIn();
const token = googleUser.getAuthResponse().id_token;
const credential = app.auth.GoogleAuthProvider.credential(token);
return this.auth.signInWithCredential(credential);
};
用户的下一步是授予应用对其日历的离线访问权限:
doConnectGoogleCalendar = async () => {
const params = {scope:scopes};
const result = await this.auth2.grantOfflineAccess(params);
console.log(result.code); // logs: "4/ygFsjdK....."
};
此时前端拥有授权代码,可以将其传递给 server-side 应用程序以交换访问和刷新令牌。我一直无法找到使用用户提供的 auth-code 来调用可用范围的好方法。这就是我配置 oauth 客户端的方式:
auth_client = Google::APIClient::ClientSecrets.load(
File.join(Rails.root,'config','client_secrets.json') // downloaded from GCP
).to_authorization
^ 我在后端使用与前端相同的 GCP 凭据。它是一种 "OAuth 2.0 Client ID" 类型的凭证。 我不确定这是否是好的做法。另外,我是否需要定义与前端相同的配置(如 access_type 和范围)?
接下来我按照文档对 get the access and refresh tokens 的说明进行操作(单击 Ruby):
auth_client.code = authorization_code_from_frontend
auth_client.fetch_access_token!
---------
Signet::AuthorizationError (Authorization failed. Server message:)
{
"error": "invalid_grant",
"error_description": "Bad Request"
}
在设置单独的后端应用程序来处理对用户授权范围的离线访问时,我是否遗漏了什么?这些库中有很多不同的信息,但我无法将其提炼成有用的信息。
更新 我发现这个页面描述了我在其他任何地方都没有找到的“one-time-code flow”,这是我浏览过的所有文档。它确实回答了我上面的一个小问题:是的,您可以使用与后端 Web 应用程序相同的客户端机密。(请参阅底部的完整示例,他们就是这样做的).我将对其进行更多探索,看看是否可以解决我更大的问题。还将更新标题以包含 one-time-code 流程。
在对代码示例和源代码进行大量挖掘之后,我有了一个干净的工作解决方案。在 "update" 中找到该页面后,我发现 ClientSecrets
我做事的方式一直是 deprecated in favor of the google-auth-library-ruby 项目。我很高兴找到它,因为它似乎是一个更完整的解决方案,因为它为您处理所有令牌管理。这是设置所有内容的代码:
def authorizer
client_secrets_path = File.join(Rails.root,'config','client_secrets.json')
client_id = Google::Auth::ClientId.from_file(client_secrets_path)
scope = [Google::Apis::CalendarV3::AUTH_CALENDAR_READONLY]
redis = Redis.new(url: Rails.application.secrets.redis_url)
token_store = Google::Auth::Stores::RedisTokenStore.new(redis: redis)
Google::Auth::WebUserAuthorizer.new(client_id, scope, token_store, "postmessage")
end
然后这就是我如何使用授权码:
def exchange_for_token(user_id,auth_code)
credentials_opts = {user_id:user_id,code:auth_code}
credentials = authorizer.get_and_store_credentials_from_code(credentials_opts)
end
调用该方法后,库会将交换的令牌存储在 Redis 中(您可以配置存储位置)以备后用,如下所示:
def run_job(user_id)
credentials = authorizer.get_credentials(user_id)
service = Google::Apis::CalendarV3::CalendarService.new
service.authorization = credentials
calendar_list = service.list_calendar_lists.items
# ... do more things ...
end
信息太多,很难区分适用于每种情况的信息。希望这可以帮助其他受困于 "one-time-code flow" 的人,这样他们就不会花几天时间在办公桌上敲头。