简体   繁体   English

3 条腿的 OAuth 和使用 google-auth-library-ruby 和 google-api-ruby-client 的一次性代码流

[英]3-legged OAuth and one-time-code flow using google-auth-library-ruby with google-api-ruby-client

Quick Overview : I have a ruby app that runs nightly and does something with a user's google calendar.快速概览:我有一个 ruby 应用程序,它每晚运行并使用用户的谷歌日历做一些事情。 The user has already given access via a separate react app.用户已经通过单独的 React 应用授予访问权限。 I'm having trouble getting the ruby app to access the user's calendar with the authorization code from the react app.我无法使用来自 React 应用程序的授权代码让 ruby 应用程序访问用户的日历。

Details : I have a React front-end that can sign in a user using gapi and subsequently sign the user into Firebase. Here is how I configure the gapi obj:详细信息:我有一个 React 前端,可以使用 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
})

Here is sign in:这是登录:

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);
};

The user's next step is to grant the app offline access to their calendar:用户的下一步是授予应用程序对其日历的离线访问权限:

doConnectGoogleCalendar =  async () => {
  const params = {scope:scopes};
  const result = await this.auth2.grantOfflineAccess(params);
  console.log(result.code); // logs: "4/ygFsjdK....."
};

At this point the front end has the authorization code that can be passed to a server-side application to be exchanged for access and refresh tokens.此时,前端具有可以传递给服务器端应用程序以交换访问和刷新令牌的授权代码。 I haven't been able to find a good way to use a user supplied auth-code to make calls to available scopes.我一直无法找到使用用户提供的授权代码来调用可用范围的好方法。 This is how I've configured the oauth client:这就是我配置 oauth 客户端的方式:

auth_client = Google::APIClient::ClientSecrets.load(
  File.join(Rails.root,'config','client_secrets.json') // downloaded from GCP
).to_authorization

^ I'm using the same GCP Credentials on the backend that I'm using for the frontend. ^ 我在后端使用与前端相同的 GCP 凭据。 It is a "OAuth 2.0 Client ID" type of credential.它是一种“OAuth 2.0 客户端 ID”类型的凭证。 I'm unsure if this is good practice or not.我不确定这是否是好的做法。 Also, do I need to define the same config that I do on the frontend (like access_type and scope)?.另外,我是否需要定义与前端相同的配置(如 access_type 和范围)?

Next I do what the docs say to get the access and refresh tokens (click Ruby):接下来我按照文档中的说明获取访问和刷新令牌(单击 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"
}

Is there something I'm missing in setting up a separate backend application that can handle offline access to a user granted scope?在设置一个单独的后端应用程序可以处理对授予 scope 的用户的离线访问时,我是否遗漏了什么? There is so much different information on these libraries but I haven't been able to distill it down to something that works.这些库中有很多不同的信息,但我无法将其提炼成有用的信息。

UPDATE I found this page describing the " one-time-code flow " which I haven't found anywhere else is all of the docs I've gone through.更新我发现这个页面描述了我在其他任何地方都没有找到的“ 一次性代码流”是我浏览过的所有文档。 It does answer one of my minor questions above: Yes, you can use the same client secrets as the web application for the backend.它确实回答了我上面的一个小问题:是的,您可以对后端使用与 web 应用程序相同的客户端机密。 (see the full example at the bottom where they do just that). (请参阅底部的完整示例,他们就是这样做的)。 I'll explore it more and see if my bigger problem can be resolved.我将对其进行更多探索,看看是否可以解决我更大的问题。 Also going to update the title to include one-time-code flow.还将更新标题以包含一次性代码流。

After a good amount of digging through code samples and source code, I have a clean working solution.在对代码示例和源代码进行大量挖掘之后,我有了一个干净的工作解决方案。 Once I found the page in my "update" it led me to finding out that ClientSecrets way I was doing things had been deprecated in favor of the google-auth-library-ruby project.一旦我在我的“更新”中找到该页面,它让我发现我做事的ClientSecrets方式已被弃用,有利于google-auth-library-ruby项目。 I'm glad I found it because it seems to be a more complete solution as it handles all of the token management for you.我很高兴找到它,因为它似乎是一个更完整的解决方案,因为它为您处理所有令牌管理。 Here is code to setup everything:这是设置所有内容的代码:

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

and then this is how I use the authorization code :然后这就是我使用授权码的方式:

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

after calling that method the library will store the exchanged tokens in Redis (you can configure where to store) for later use like this:调用该方法后,库会将交换的令牌存储在 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

There is so much info out there that it is difficult to isolate what applies to each condition.那里的信息太多了,很难区分适用于每种情况的信息。 Hopefully this helps anyone else that gets stuck with the "one-time-code flow" so they don't spend days banging their head on their desk.希望这能帮助其他陷入“一次性代码流”的人,这样他们就不会花几天时间在办公桌上敲头。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 无法将 3-legged OAuth 与 XACML 集成 - Unable to integrate 3-legged OAuth with XACML 在谷歌云医疗保健 API (Ruby) 中创建 FHIR 资源时出错 - Error when creating an FHIR resource in google cloud healthcare API (Ruby) 将使用 google-api-python-client 库的 Python 脚本部署为 Google Cloud Function - Deploying Python script that use google-api-python-client library as Google Cloud Function 正确的 Google API 和 OAuth 2.0 重定向 URI - Correct redirect URI for Google API and OAuth 2.0 Google API 客户端机密错误(Python) - Google API client secrets error (Python) 用户 Google Api 与 Firebase 身份验证 - User Google Api with Firebase auth FCM 仅通过使用 PHP 的 Google 客户端 API 为 iOS 设备返回 401 未经授权的错误 - FCM returning 401 unauthorized error for iOS devices only with Google client API using PHP 如何使用 JAVA 库在 Google API 上获取用户配置文件? - How to get user profile on Google API using the JAVA library? 如何使用 Python 中的服务帐户向 Google Cloud 进行身份验证? - How to Auth to Google Cloud using Service Account in Python? google.auth.exceptions.DefaultCredentialsError: - google.auth.exceptions.DefaultCredentialsError:
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM