检测何时撤销了Google API的oAuth令牌

时间:2019-10-03 17:42:11

标签: python-3.x oauth oauth-2.0 google-api google-oauth2

我正在从这里使用Google的python快速入门脚本:https://developers.google.com/calendar/quickstart/python

在正常情况下效果很好。 我可以允许它访问我的Gmail帐户,并且可以创建Google日历条目。

但是,如果我在浏览器中登录我的Gmail帐户,请转到“安全性”,然后撤消我的应用程序对Google帐户的访问权限,则快速启动脚本将无法使用。它只是崩溃了:

$ python quickstart.py
Getting the upcoming 10 events
Traceback (most recent call last):
  File "quickstart.py", line 52, in <module>
    main()
  File "quickstart.py", line 42, in main
    orderBy='startTime').execute()
  File "/opt/my_project/venv3/lib/python3.6/site-packages/googleapiclient/_helpers.py", line 130, in positional_wrapper
    return wrapped(*args, **kwargs)
  File "/opt/my_project/venv3/lib/python3.6/site-packages/googleapiclient/http.py", line 851, in execute
    method=str(self.method), body=self.body, headers=self.headers)
  File "/opt/my_project/venv3/lib/python3.6/site-packages/googleapiclient/http.py", line 165, in _retry_request
    resp, content = http.request(uri, method, *args, **kwargs)
  File "/opt/my_project/venv3/lib/python3.6/site-packages/google_auth_httplib2.py", line 213, in request
    self.credentials.refresh(self._request)
  File "/opt/my_project/venv3/lib/python3.6/site-packages/google/oauth2/credentials.py", line 136, in refresh
    self._client_secret))
  File "/opt/my_project/venv3/lib/python3.6/site-packages/google/oauth2/_client.py", line 237, in refresh_grant
    response_data = _token_endpoint_request(request, token_uri, body)
  File "/opt/my_project/venv3/lib/python3.6/site-packages/google/oauth2/_client.py", line 111, in _token_endpoint_request
    _handle_error_response(response_body)
  File "/opt/my_project/venv3/lib/python3.6/site-packages/google/oauth2/_client.py", line 61, in _handle_error_response
    error_details, response_body)
google.auth.exceptions.RefreshError: ('invalid_grant: Token has been expired or revoked.', '{\n  "error": "invalid_grant",\n  "error_description": "Token has been expired or revoked."\n}')

如果我在引发异常的行之前添加一些调试行,则会看到以下内容:

creds.valid: True
creds.expired: False

我知道我可以赶上google.auth.exceptions.RefreshError。我遇到的问题是,在quickstart.py的开头有一整段代码,目的是检测令牌是否有效,但是似乎无法检测到这种相当简单的情况,并且直到抛出异常为止我实际上尝试使用令牌来执行命令。如果脚本的第一部分在检测令牌被吊销时无法完成工作,那么将每个执行(或至少第一个)放在try-except块中似乎很愚蠢。

在我实际尝试使用证书之前,是否有办法检测到该证书已被吊销?为什么creds.valid和creds.expired无法正常工作?

更新:如果您等待足够长的时间(几分钟或几小时-不确定),则creds.valid和creds.expired最终将显示凭据不再有效。但是,在这段时间之间,如果凭据似乎有效但无法使用,就足以使我的程序在处理不当时崩溃。

更新2:我唯一能想到的就是像这样运行:

from googleapiclient.discovery import build
from google.auth.exceptions import RefreshError

...
try:
    service = build('calendar', 'v3', credentials=creds)
    service.events().list(calendarId='primary', maxResults=1).execute()
except RefreshError:
    ...
...

在检查凭据是否有效的代码之后。有点像最终检查。这行得通,但似乎有点脏,因为它需要对Google API服务器进行额外的调用,并且基本上就是我上面所说的-将第一个执行放在try-except块中。有没有更好的办法?

1 个答案:

答案 0 :(得分:1)

快速入门适用于具有您自己的凭据的简单情况。他们显然不希望您撤销自己的授权。而不是仅在凭据过期后才刷新凭据,而是在每次启动时刷新访问令牌。如果刷新令牌已被撤消,则第一次刷新将是失败点。

/* eslint-disable @typescript-eslint/camelcase */
import {
  repository
} from '@loopback/repository';
import {
  post,
  getModelSchemaRef,
  requestBody,
  HttpErrors,
} from '@loopback/rest';
import { Waitlist } from '../models';
import { WaitlistRepository } from '../repositories';
import { validateCredentialsWaitlist } from '../services/validator';
import _ = require('lodash');
//const app = require('loopback-component-mailchimp')
import { keepSubscribers } from '../services/MailChimp'

export class WaitlistControllerController {
  constructor(
    @repository(WaitlistRepository)
    public waitlistRepository: WaitlistRepository,
  ) { }

  @post('/waitlists', {
    responses: {
      '200': {
        description: 'Waitlist model instance',
        content: { 'application/json': { schema: getModelSchemaRef(Waitlist) } },
      },
    },
  })
  async create(
    @requestBody({
      content: {
        'application/json': {
          schema: getModelSchemaRef(Waitlist),
        },
      },
    })
    waitlist: Waitlist,
  ): Promise<Waitlist> {
    const foundUser = await this.waitlistRepository.findOne({
      where: {
        email: waitlist.email,
      },
    });
    if (foundUser) {
      throw new HttpErrors.Forbidden(`Email ${waitlist.email} already exists`);
    }

    await keepSubscribers(waitlist.email)
    validateCredentialsWaitlist(_.pick(waitlist, ['email', 'firstName', 'secondName']));
    return this.waitlistRepository.create(waitlist);
  }
}