【问题标题】:Google Identity Services : How to refresh access_token for Google API after one hour?Google 身份服务:如何在一小时后刷新 Google API 的 access_token?
【发布时间】:2023-02-08 01:21:55
【问题描述】:

我已经实现了新的 Google Identity Services 来获取 access_token 来调用 Youtube API。 我尝试在 Angular 应用程序上使用它。

this.tokenClient = google.accounts.oauth2.initTokenClient({
  client_id: googleApiClientId,
  scope: 'https://www.googleapis.com/auth/youtube.readonly',
  callback: (tokenResponse) => {
    this.accessToken = tokenResponse.access_token;
  },
});

当我调用 this.tokenClient.requestAccessToken() 时,我可以获得一个访问令牌并使用 Youtube API,这是可行的。

一个小时以后,此令​​牌过期。我有这个错误:"Request had invalid authentication credentials."

如何为用户透明地获取新刷新的 access_token ?

【问题讨论】:

    标签: api authentication oauth-2.0 authorization access-token


    【解决方案1】:

    Google 身份服务 (GIS) 库有 two authorization flows

    1. 隐式流,仅在客户端使用 .requestAccessToken()
    2. 授权代码流程,它也需要后端(服务器端)并使用.requestCode()

      对于隐式流程(您正在使用的流程),没有刷新令牌。由客户端检测令牌过期并重新运行令牌请求流。以下是来自谷歌示例的一些示例代码,用于说明如何处理此问题:

      // initialize the client
      tokenClient = google.accounts.oauth2.initTokenClient({
          client_id: 'YOUR_CLIENT_ID',
          scope: 'https://www.googleapis.com/auth/calendar.readonly',
          prompt: 'consent',
          callback: '',  // defined at request time in await/promise scope.
      });
      
      // handler for when token expires
      async function getToken(err) {
        if (err.result.error.code == 401 || (err.result.error.code == 403) &&
            (err.result.error.status == "PERMISSION_DENIED")) {
      
          // The access token is missing, invalid, or expired, prompt for user consent to obtain one.
          await new Promise((resolve, reject) => {
            try {
              // Settle this promise in the response callback for requestAccessToken()
              tokenClient.callback = (resp) => {
                if (resp.error !== undefined) {
                  reject(resp);
                }
                // GIS has automatically updated gapi.client with the newly issued access token.
                console.log('gapi.client access token: ' + JSON.stringify(gapi.client.getToken()));
                resolve(resp);
              };
              tokenClient.requestAccessToken();
            } catch (err) {
              console.log(err)
            }
          });
        } else {
          // Errors unrelated to authorization: server errors, exceeding quota, bad requests, and so on.
          throw new Error(err);
        }
      }
      
      // make the request
      function showEvents() {
        // Try to fetch a list of Calendar events. If a valid access token is needed,
        // prompt to obtain one and then retry the original request.
      
        gapi.client.calendar.events.list({ 'calendarId': 'primary' })
        .then(calendarAPIResponse => console.log(JSON.stringify(calendarAPIResponse)))
        .catch(err  => getToken(err))  // for authorization errors obtain an access token
        .then(retry => gapi.client.calendar.events.list({ 'calendarId': 'primary' }))
        .then(calendarAPIResponse => console.log(JSON.stringify(calendarAPIResponse)))
        .catch(err  => console.log(err));   // cancelled by user, timeout, etc.
      }
      

      不幸的是,GIS 不会像 GAPI 那样为您处理任何令牌刷新,因此您可能希望将您的访问包装在一些常见的重试逻辑中。

      重要的是状态代码将是401403,状态将是PERMISSION_DENIED

      您可以查看此示例的详细信息here,切换到异步/等待选项卡以查看完整代码。

    【讨论】:

    • 谢谢你,就目前而言,很清楚。根据我的经验,再次调用 tokenClient.requestAccessToken() 会为用户带来相同的用户体验 - 用户会再次被要求以交互方式重新选择他们想要使用的帐户。这是一次不幸的经历。关于避免这种情况的任何提示?
    • @Cheeso - 是的,这真的很有挑战性。 this question 中对此有更多讨论,可能会有帮助。您可以提示用户并使用 prompt: '' 使弹出窗口自动选择,但我目前的理解是,要完全避免它,您必须使用后端并使用授权代码流。如果您找到更好的解决方案,我很想听听。
    • 如果这可以节省任何人的时间(我花了一段时间才弄清楚),如果您确实迁移到授权代码流程并且您正在使用弹出窗口获取授权代码,则需要使用 "postmessage" 作为 redirect_uri您的授权码 -> 令牌请求。 More details here
    【解决方案2】:

    要以对最终用户透明的方式刷新访问令牌,您必须使用刷新令牌,此令​​牌也将响应您的呼叫。

    使用此令牌,您可以使用以下请求正文对 URL 进行 POST 调用:https://www.googleapis.com/oauth2/v4/token

    client_id: <YOUR_CLIENT_ID>
    client_secret: <YOUR_CLIENT_SECRET>
    refresh_token: <REFRESH_TOKEN_FOR_THE_USER>
    grant_type: refresh_token
    

    刷新令牌永不过期,因此您可以多次使用它。响应将是这样的 JSON:

    {
      "access_token": "your refreshed access token",
      "expires_in": 3599,
      "scope": "Set of scope which you have given",
      "token_type": "Bearer"
    }
    

    【讨论】:

    • 您能否提供有关如何执行此操作的更多详细信息?使用问题中的 initTokenClient 方法时,响应不包含 refresh_token 字段,仅包含 access_tokenexpires_inscopetoken_type
    • 有同样的问题。新库没有给出任何关于如何静默刷新用户会话的提示。调用 requestAccessToken 显示弹出窗口
    • @levgen,你解决了这个问题吗?
    • 这个答案无处不在。但是,如何获得刷新令牌?它不是从 initTokenClient 方法返回的。这是互联网上没有人回答的问题。
    • stackoverflow.com/users/1841839/daimto 我看到你是谷歌 api 专家,因为你已经将我的问题标记为重复 (stackoverflow.com/questions/74303317/…),请你在这里给我们点灯好吗?
    【解决方案3】:

    @victor-navarro 的回答是正确的,但我认为 URL 是错误的。 我用这样的身体向 https://oauth2.googleapis.com/token 发出了一个 POST 调用,它对我有用:

    client_id: <YOUR_CLIENT_ID>
    client_secret: <YOUR_CLIENT_SECRET>
    refresh_token: <REFRESH_TOKEN_FOR_THE_USER>
    grant_type: refresh_token
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-10-05
      • 2020-04-22
      • 1970-01-01
      • 2017-08-06
      • 2020-05-20
      • 2016-10-21
      • 2021-11-01
      相关资源
      最近更新 更多