【シェルと遊ぼう】Google APIへOAuth2.0でアクセスするためのシェルスクリプト

はじめに

NeoMuttからGmailを使うには、OAuth2.0による認可が必要。

Neomuttの設定ファイルでOAuth2.0の「アクセストークン」を更新するコマンドを指定すればいいらしい。

そこで、使えるコマンドを探してみたら、下のPythonのスクリプトを見つけた。

だけれども、今後しばらく使うであろうメール環境。
わざわざPythonに依存したくない。保守したくない。

それなら作ろうシェルスクリプト。

ということで、Google APIで利用するOAuth2.0の「アクセストークン」と
「リフレッシュトークン」を取得、更新するシェルスクリプトを作ってみたよ。

そもそもOAuth2.0とはなんぞやという人は下の記事を読んでみてね。

作るもの

以下の機能に対応する。

[USAGE]
  ggl-oauth2 -g [-b browser] [-nv] <Account-file>     # 認可ページのURIを生成
  ggl-oauth2 -i <token> | <Account-file>              # 指定トークンに関する情報取得
  ggl-oauth2 -n [-v] <Account-file>                   # 新規トークンの取得
  ggl-oauth2 -r <token> | <Account-file>              # 指定トークンを削除
  ggl-oauth2 -u [-v] <Account-file>                   # 指定トークンの更新
  ggl-oauth2 [-hV]                                    # ヘルプ/バージョンの表示

ちなみに、curlには依存しちゃうので、インストールしておいてね。

# FreeBSDの場合
$ sudo pkg install curl

事前準備

1.クライアントの登録

まず、対象のGoogleアカウントでOAuthを利用するアプリケーションをクライアントとして登録しておく必要がある。
下の記事を参照されたし。

2. アカウント情報ファイルの作成

登録が終わったら、今回のスクリプトで利用する以下のフォーマットの<Acount-file>を用意する。
ファイル名は好きにしてね。

# OAuth2.0 Client setting
SCOPE=https://mail.google.com/
CLIENT_ID=登録時に発行されたID
CLIENT_SECRET=登録時に発行されたシークレット

ファイルのアクセス権は600(自分以外読み書き不可)が好ましいかと。

$ chmod 600 "<Account-file>"

コード

今回のコードは長めなので抜粋で機能ごとに説明する。

今回のコード全体は下に置いた。

下のコマンドとかでローカルに落として下さいまし。

$ git clone https://github.com/hacolab/google-oauth2-sh.git

認可ページのURIを生成

-gオプションで認可用のURIを生成する。

生成したURIはデフォルトだと標準出力に出力するだけ。
これをブラウザにコピペしてアクセスする感じ。

ブラウザでの認可の手順は以下を参考にされたし。

該当のコードは以下。

# Generate authorization-code
if [ -n "$MODE_GENERATE_AUTH_URI" ]; then
  "$BROWSER_CMD" "${AUTH_END_POINT}?client_id=${CLIENT_ID}&scope=${SCOPE}&response_type=code&access_type=offline&redirect_uri=${REDIRECT_URI}"
fi

-b <Browser>オプションで指定したコマンドが$BROWSER_CMDに入る。
${CLIENT_ID}は事前準備で用意した<Account-file>から読み取っている。
他の変数は、ソースの上の方で定義している。

新規トークンの取得

-nオプションで、認可コードを入力待ち状態になる。

認可時に表示された認可コードを認可してReturnキーを押してねと。

入力された認可コードで「アクセストークン」と「リフレッシュトークン」を取得する。

該当のコードは以下。

# Get new access-token & refresh-token
if [ -n "$MODE_AUTHORIZATION" ]; then
  printf "please input authorization-code: "
  read auth_code
  [ -z "$auth_code" ] && error_exit "not input authorization-code!"

  # get access-token & refresh-token
  curl -s --verbose                   \
    -d grant_type=authorization_code  \
    -d client_id=$CLIENT_ID           \
    -d client_secret=$CLIENT_SECRET   \
    -d redirect_uri=$REDIRECT_URI     \
    -d code=$auth_code                \
    $TOKEN_END_POINT > "$HTTP_LOG" 2>&1

  check_response "$HTTP_LOG" "authorization faild!"

  ACCESS_TOKEN=$(pickup_value access_token < "$HTTP_LOG")
  REFRESH_TOKEN=$(pickup_value refresh_token < "$HTTP_LOG")
  write_config "$ACCOUNT_FILE" REFRESH_TOKEN "$REFRESH_TOKEN"
fi

正しい認可コードが確認できてら、「アクセストークン」を標準出力に出力し、
「リフレッシュトークン」を<Account-file>に追記・更新する。

-vオプションを指定すれば、curlコマンドのログと「リフレッシュトークン」も標準出力に出力する。

elif [ -n "$VERBOSE_LOG" ]; then
  # Print infomation
  [ -f "$HTTP_LOG" ] && cat "$HTTP_LOG" | grep ^
  echo "access_token: $ACCESS_TOKEN"
  echo "refresh_token: $REFRESH_TOKEN"

トークンの更新

-uオプションで、新しい「アクセストークン」を取得できる。

該当のコードは以下。

# Update access-token
if [ -n "$MODE_UPDATE_TOKEN" ]; then
  curl -s --verbose                   \
    -d grant_type=refresh_token       \
    -d client_id=$CLIENT_ID           \
    -d client_secret=$CLIENT_SECRET   \
    -d refresh_token=$REFRESH_TOKEN   \
    $TOKEN_END_POINT > "$HTTP_LOG" 2>&1

  check_response "$HTTP_LOG" "access_token update faild!"

  ACCESS_TOKEN=$(pickup_value access_token < "$HTTP_LOG")
fi

-vオプション指定時はcurlのログと「リフレッシュトークン」を標準出力に出力する。

取得済トークンに関する情報取得

-iオプションで、指定の「アクセストークン」の情報を取得できる。
引数には「アクセストークン」を指定するか、<Account-file>を指定する。

該当のコードは以下。

if [ -n "$MODE_INQUIRE_SCOPE" ]; then
  # Inquire infomation
  curl -s "https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=${ACCESS_TOKEN}" \
    | grep "^"

grepをつけているのは、出力に改行をつけるため。

出力は以下みたいな感じ。
返ってきたHTTPレスポンスのJSONをそのまま出力。

{
  "issued_to": "123456789012-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com",
  "audience": "123456789012-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com",
  "scope": "https://mail.google.com/",
  "expires_in": 2466,
  "access_type": "offline"
}

現在のトークンを削除

-rオプションで、指定の「アクセストークン」と、それに紐付けられた「リフレッシュトークン」を削除する。
引数には「アクセストークン」か<Account-file>を指定する。

該当のコードは以下。

elif [ -n "$MODE_REVOKE_TOKEN" ]; then
  # Revoke access-token & refresh-token
  curl -s "https://accounts.google.com/o/oauth2/revoke?token=${ACCESS_TOKEN}" \
    | grep "^"

grepをつけているのは、出力に改行をつけるため。

削除成功後は同じトークンを使えないため、ブラウザによる認可作業から行う必要がある。

さいごに

長くなったので今回はここまで。

ちょっと変数を増やしすぎたのは反省点ではあるけれども、ま、いっか。

そして、POSIXには無いsed-iオプションを使ってしまっていることに気付く。。。
気が向いた時にこっそり直そう。

ちなみに下のように認可エンドポイントのURIにバージョン(v2)が含まれているっぽいけど、
どのバージョンを使うべきなのかよく分からんとです。
ご存知の方がいらっしゃいましたら、コメント頂けると助かります。

AUTH_END_POINT=https://accounts.google.com/o/oauth2/v2/auth

さて次回は、このスクリプトを利用するサンプルスクリプトを作ってみよう。


以上。

確認環境

PC Thinkpad X1 Carbon 2nd Gen
OS FreeBSD 12.1-RELEASE-p8

参考