谷歌身份验证服务: 如何在一小时后为谷歌API刷新access_token?

4

我已经实现了新的Google身份验证服务以获取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,它可以正常工作。

但是一个小时后,该令牌会过期。我会收到此错误:"请求具有无效的身份验证凭据。"

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

3个回答

3
Google Identity Services(GIS)库有两种授权流程
  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那样为您处理任何令牌刷新,因此您可能需要将访问包装在一些常见的重试逻辑中。
重要的部分是状态代码将是401或403,状态将是PERMISSION_DENIED。
您可以在这里查看此示例的详细信息,切换到async/await选项卡以查看完整代码。

3
谢谢您的回复,就目前而言已经很清楚了。根据我的经验,再次调用tokenClient.requestAccessToken()会导致用户看到相同的UX - 用户需要重新交互式地选择他们想要使用的帐户。这是一个不幸的体验。有什么避免这种情况的提示吗? - Cheeso
1
@Cheeso - 是的,这确实很具有挑战性。在这个问题中有更多关于此的讨论,可能会有所帮助。您可以提示用户并使用prompt:'',这使弹出窗口自动选择,但我目前的理解是,为了完全避免它,您必须使用后端并采用授权代码流程。如果您找到更好的解决方案,我很乐意听取。 - Matt Sanders
如果这篇文章能够帮助到任何人(我自己也花了一些时间才弄明白),如果你迁移到授权码流程并且使用弹出窗口来获取授权码,那么你需要将"postmessage"作为你的授权码->令牌请求的redirect_uri更多细节请参见此处 - Matt Sanders

0
为了让最终用户能够透明地刷新访问令牌,您需要使用刷新令牌,该令牌也将在响应中返回给您的调用。
有了这个令牌,您可以对以下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"
}

6
你能提供更多关于如何操作的细节吗?当使用问题中的 initTokenClient 方法时,响应中并没有包含 refresh_token 字段,只有 access_tokenexpires_inscopetoken_type - istvan.halmen
1
遇到了同样的问题。新库没有给出任何提示如何静默刷新用户会话。调用requestAccessToken会显示弹窗。 - Ievgen
1
@levgen,你解决了这个问题吗? - Vishal Kiri
这个答案无处不在。但是,如何获取刷新令牌?它并没有从initTokenClient方法返回。这是互联网上没有人回答的问题。 - Dhevendhiran M
大家好,你们找到这个问题的答案了吗? - Realizt30
1
嗨,https://stackoverflow.com/users/1841839/daimto ,我看到你是一位谷歌API专家,而且因为你标记了我的问题为重复问题(https://stackoverflow.com/questions/74303317/how-to-manage-google-identity-service-access-token),请您能否在这里给我们提供一些帮助? - Realizt30

0

@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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接