Google Apps ScriptのOAuth認証URLとリフレッシュトークン有効期限を調査
きっかけ
Gmail関連のアップデートの影響か、最近FileMakerからGmailのSMTPサーバーを使ってメール送信していた環境で、「送信エラーになる」という話題をTwitterなどでよく見かけるようになりました。
対策として、Google Apps Script (GAS) を経由してメールを送信する方法が考えられますが、そこで気になるのが「認証トークンの有効期限」です。もし頻繁に再認証(再同意)が必要になるようでは、自動化の運用に乗せるのが難しくなります。
そこで、GASがバックグラウンドでどのようなOAuth認証URLを発行し、アクセストークンを取得しているのか詳しく調べてみました。
GASの認証URLを確認する
実際にGASのエディタでスクリプトを実行し、初回認証が必要になった際に生成されるURLを取得して解析してみます。
function myFunction() {
// 認証ダイアログを出すために、明示的にトークンを取得するメソッドを実行
const token = ScriptApp.getOAuthToken();
console.log(token);
}
スクリプト実行時に表示される認証用URLを確認した結果がこちらです。
取得したURL
const url = new URL(`https://accounts.google.com/o/oauth2/auth/oauthchooseaccount?client_id=123456789123-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com&redirect_uri=https%3A%2F%2Fscript.google.com%2Foauthcallback&state=13464014877097984000&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fgmail.send&response_type=none%20gsession&access_type=offline&approval_prompt=force&hl=ja&login_hint=sample%40gmail.com&service=lso&o2v=1&theme=mn&ddm=0&flowName=GeneralOAuthFlow`);
console.log(url);
URLをパースした結果
取得したURLをNode.jsなどでパース(解析)してみると、以下の構造になっていることがわかります。
URL {
href: 'https://accounts.google.com/o/oauth2/auth/oauthchooseaccount?client_id=123456789123-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com&redirect_uri=https%3A%2F%2Fscript.google.com%2Foauthcallback&state=13464014877097984000&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fgmail.send&response_type=none%20gsession&access_type=offline&approval_prompt=force&hl=ja&login_hint=sample%40gmail.com&service=lso&o2v=1&theme=mn&ddm=0&flowName=GeneralOAuthFlow',
origin: 'https://accounts.google.com',
protocol: 'https:',
username: '',
password: '',
host: 'accounts.google.com',
hostname: 'accounts.google.com',
port: '',
pathname: '/o/oauth2/auth/oauthchooseaccount',
search: '?client_id=123456789123-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com&redirect_uri=https%3A%2F%2Fscript.google.com%2Foauthcallback&state=13464014877097984000&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fgmail.send&response_type=none%20gsession&access_type=offline&approval_prompt=force&hl=ja&login_hint=sample%40gmail.com&service=lso&o2v=1&theme=mn&ddm=0&flowName=GeneralOAuthFlow',
searchParams: URLSearchParams {
'client_id' => '123456789123-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com',
'redirect_uri' => 'https://script.google.com/oauthcallback',
'state' => '13464014877097984000',
'scope' => 'https://www.googleapis.com/auth/gmail.send',
'response_type' => 'none gsession',
'access_type' => 'offline',
'approval_prompt' => 'force',
'hl' => 'ja',
'login_hint' => 'sample@gmail.com',
'service' => 'lso',
'o2v' => '1',
'theme' => 'mn',
'ddm' => '0',
'flowName' => 'GeneralOAuthFlow' },
hash: ''
}
パラメータを見ると、access_type に offline が設定されていることがわかります。
これは、「リフレッシュトークン(更新トークン)」を発行するという指定です。Google内部でこのリフレッシュトークンが管理されており、アクセストークンの期限が切れても、自動的に更新してくれる仕組みになっていると思われます。
リフレッシュトークンの有効期限について
ここで少し懸念点があります。通常、Google Cloud Platform (GCP) で外部アプリとして同意画面を設定し、ステータスが「テスト中」の場合、リフレッシュトークンが7日後に期限切れになるという仕様があるからです。
外部ユーザータイプ用に OAuth 同意画面が構成され、公開ステータスが「テスト中」である Google Cloud Platform プロジェクトには、7 日後に期限切れになる更新トークンが発行されます。
ただし、リクエストされた OAuth スコープが、名前、メールアドレス、ユーザー プロファイル( userinfo.email, userinfo.profile, openid スコープ、または OpenID Connect の同等のもの)のサブセットである場合を除きます。
しかし、今回のGASは、個別のGCPプロジェクトを紐付けず、GAS標準の「デフォルトのCloudプロジェクト」を使用しています。
この場合、テストモード扱いにはならず、7日後になっても期限が切れないというのが一般的な理解です。もしこれが切れてしまうと、世界中のGASのトリガー実行が毎週止まってしまうことになるためです。
おそらく期限切れは発生しないと考えていますが、確実な検証のため、7日後に再度スクリプトを実行して確認してみたいと思います。

