Google API で Token has been expired or revoked. のエラー

Google Driveへのバックアップをするプログラムを作って動かしていたら “Token has been expired or revoked.” というエラーが出て失敗するようになった。

google.auth.exceptions.RefreshError: ('invalid_grant: Token has been expired or revoked.', {'error': 'invalid_grant', 'error_description': 'Token has been expired or revoked.'})

原因

GCPのドキュメントを読むと「OAuth同意画面が外部ユーザータイプ用に構成され、公開ステータスが「テスト中」のGoogle Cloud Platformプロジェクトには、7日で有効期限が切れる更新トークンが発行されます。」とあり、まさにこれに該当していました。

  • OAuth 2.0 クライアント ID を利用していて、
  • OAuth 同意画面の公開ステータスが「テスト」のままで(未公開で)、
  • 同じく同意画面のユーザーの種類が「外部」

だったので、作成から7日で無効になったということです。

対策

今回はバックアップ用途で別にユーザーにログインさせたいわけでもないので、OAuth 2.0 クライアントID ではなく、サービスアカウントを利用することで解決できました。

  1. GCP > 認証情報 で「サービスアカウント」を1つ作る。設定は初期値でだいたいOK
  2. 作成したサービスアカウントの「キー」を作る。形式は JSON を選択する > 認証情報の入った json ファイルをダウンロードできる
    ※再ダウンロードする場合はキーの作り直しになるので注意
  3. プログラムが使う認証情報をダウンロードしたjsonファイルに差し替える
  4. Googleドライブのフォルダーに対して、サービスアカウントのメールアドレスに共有の権限(閲覧者)を付与する ←忘れがち

これでだいたい上手くいくと思いますが、 “Client secrets must be for a web or installed app.” などと新しいエラーが発生する場合は、こちらの記事も参照ください。