前回 書いたコード、しばらく経ってからアクセスすると AccessTokenRefreshError なるエラーが発生することがわかった。
ERROR 2015-03-13 15:12:12,692 base.py:210] Internal Server Error: /task_manager/
Traceback (most recent call last):
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/lib/django-1.5/django/core/handlers/base.py", line 113, in get_response
response = callback(request, *callback_args, **callback_kwargs)
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/lib/django-1.5/django/views/generic/base.py", line 68, in view
return self.dispatch(request, *args, **kwargs)
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/lib/django-1.5/django/views/generic/base.py", line 86, in dispatch
return handler(request, *args, **kwargs)
File "/Users/yama/workspace/GAE/django_apps/utils/login.py", line 25, in check_login
return handler_method(self, request, *args)
File "/Users/yama/workspace/GAE/django_apps/task_manager/views.py", line 54, in get
tasks = service.tasks().list(tasklist='@default').execute(http=http)
File "/Users/yama/workspace/GAE/django_apps/libs/oauth2client/util.py", line 132, in positional_wrapper
return wrapped(*args, **kwargs)
File "/Users/yama/workspace/GAE/django_apps/libs/apiclient/http.py", line 716, in execute
body=self.body, headers=self.headers)
File "/Users/yama/workspace/GAE/django_apps/libs/oauth2client/util.py", line 132, in positional_wrapper
return wrapped(*args, **kwargs)
File "/Users/yama/workspace/GAE/django_apps/libs/oauth2client/client.py", line 494, in new_request
self._refresh(request_orig)
File "/Users/yama/workspace/GAE/django_apps/libs/oauth2client/client.py", line 663, in _refresh
self._do_refresh_request(http_request)
File "/Users/yama/workspace/GAE/django_apps/libs/oauth2client/client.py", line 710, in _do_refresh_request
raise AccessTokenRefreshError(error_msg)
AccessTokenRefreshError: invalid_grant

どうやら OAuth2.0 のアクセストークンの有効期限が切れていた時に当該エラーを投げている様子。
また、エラーログを見る限りエラーを raise しているのは
tasks = service.tasks().list(tasklist='@default').execute(http=http)
という行。
これを回避するには、こちらのサイト
のようにエラーを raise する可能性があるところで補足してやる方法もあるみたいだけど、
こちらの公式のサンプル
https://developers.google.com/api-client-library/python/auth/web-app#example
によると、oauth2client.client.OAuth2Credentials クラス(これは StorageByKeyName() から get したオブジェクト)には
そのへんの有効期限を判定するための access_token_expired というプロパティがあるらしい。
実際のコードを見てみる。
oauth2client/client.py
@property def access_token_expired(self): """True if the credential is expired or invalid. If the token_expiry isn't set, we assume the token doesn't expire. """ if self.invalid: return True if not self.token_expiry: return False now = datetime.datetime.utcnow() if now >= self.token_expiry: logger.info('access_token is expired. Now: %s, token_expiry: %s', now, self.token_expiry) return True return False
内部で self.invalid もやってる様子。
というわけで、既存の credentials.invalid をこっちのプロパティで置き換えてみた。
before
if credentials is None or credentials.invalid: # --- before --- FLOW.params['state'] = xsrfutil.generate_token( settings.SECRET_KEY, user) authorize_url = FLOW.step1_get_authorize_url() logging.info(authorize_url) return HttpResponseRedirect(authorize_url) else: (Tasks API を使って処理)
after
if credentials is None or credentials.access_token_expired: # --- after --- FLOW.params['state'] = xsrfutil.generate_token( settings.SECRET_KEY, user) authorize_url = FLOW.step1_get_authorize_url() logging.info(authorize_url) return HttpResponseRedirect(authorize_url) else: (Tasks API を使って処理)
しばらくこれで様子見。
TODO
アクセストークンの有効期限ってどこかに明記されてるのかな。。。
リファレンス
OAuth2 のフローについて(日本語) http://g-master.appspot.com/article/30001/50001/50002.html