

Merpay Advent Calendar 2019 の 23日目は、IDPチーム の @kokukuma がお送りします。
はじめに
Sign in with Apple(以降、SIWA)、コマンドラインツールでも使いたくありませんか?使いたいですよね?わかります。特定のAppleID持っている人じゃないと使えないコマンドラインツールとか作りたいですよね。
さてこのようなコマンドを作るためには何が必要でしょうか?ネックになるのは、認可コードをどうやって自分のコマンドラインツールに引き渡すのか?というところです。AndroidやiOSではDeeplinkの仕組みを使って、Redirect経由でアプリに認可コードを引き渡しています。
しかし、OSXではこの仕組みは利用できません。とりあえず、gcloud auth login仕組みを調べてみました。また、SIWAの紹介は随分出ているので説明は省きます。
gcloud auth loginは、どうなってるの?
下記のコマンドを実行すると、ブラウザが立ち上がって、Google認証のページと、認可画面が出てきます。その間、コマンドライン上は、待受状態になっていて、認可が終了すると先に進みます。まぁ想像はつきますが、想像がつかない体で記事を書いているので、一応コードを読んで確認しておきます。
$ gcloud auth login
Google-cloud-sdkはpythonで書かれているので、gcloud コマンドが使える環境なら、コードを確認することができると思います。auth loginコマンドは、lib/surface/auth/login.py
から追っていく事ができます。大まかな流れは、以下のような流れになっていました。この流れは、lib/googlecloudsdk/core/credentials/flow.py
を見るのが一番わかりやすそうです。
- 認可リクエストの作成
- HTTPサーバの起動/ブラウザの起動
- 認可コードの取得
- クレデンシャルの取得/保存
認可リクエストの作成
最初に認可リクエストの作成をしています。実行した内容を見ているとわかるように、redirect_uriには http://localhost:8085
が設定されています。
https://accounts.google.com/o/oauth2/auth? client_id=32555940559.apps.googleusercontent.com redirect_uri=http://localhost:8085/ response_type=code code_challenge=2HFxG2eBeRyivwzPmfZANRmugSOl_XJSEmgAR1kabo8 code_challenge_method=S256 access_type=offline prompt=select_account scope=https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/appengine.admin https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/accounts.reauth
HTTPサーバの起動/ブラウザの起動
次に、HTTPサーバをlocalhost:8085で起動し、認可サーバからのレスポンスを受けれる状態にします。これをした上で、ブラウザを起動し、Googleの認可エンドポイントにアクセスしています。このあとユーザが、ブラウザ上でGoogle認証とGoogle Cloud SDKのGoogleアカウントへの認可を行います。
認可コードの取得
認可が終了すると、設定したredirect_uriに認可コードが送られてきます。
"hd": "mercari.com" "session_state": "********************" "prompt": "consent" "code": "(認可コード)" "scope": "email https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/appengine.admin https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/accounts.reauth openid" "authuser": "1"
クレデンシャルの取得/保存
認可コードが手に入ったあとは、トークンエンドポイントを叩いて、アクセストークンやリフレッシュトークン、ID Tokenなどを取得して、それをローカルに保存し、デフォルトの認証情報を書き換えています。
https://www.googleapis.com/oauth2/v4/token? client_id=32555940559.apps.googleusercontent.com grant_type=authorization_code code=(認可コード) redirect_uri=http://localhost:8085/ client_secret=(client secret) code_verifier=******************* scope=https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/appengine.admin https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/accounts.reauth
"access_token": "(アクセストークン)", "expires_in": 3600, "refresh_token": "(リフレッシュトークン)", "scope": "https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/appengine.admin https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/accounts.reauth https://www.googleapis.com/auth/userinfo.email openid", "token_type": "Bearer", "id_token": "(JWT)"
長々と説明しましたが、肝になるのは、『auth endpointに送るredirect_uriをlocalhostにして、localhostでhttp server立ててauth code受け取る』です。
SIWAで同じことできる?
SIWAでは、redirect_uriに設定できるのは、httpsを使った検証されたドメインだけで、localhostをredirect_uriに設定することはできません。仕方ないのでAppleに設定したredirect先から、更にlocalhostにredirectしました。図にすると以下のような感じです。また、response modeはform_postにしておきました。このresponse modeとは、認可レスポンスの受け取り方を指定するもので、form_postの他に、queryやfragmentがあります。名の通り、queryの場合はクエリパラメータとして、fragmentの場合はフラグメントとして、認可コードが送られます。一方form_postの場合は、POSTで送られるわけですが、認可レスポンスとしてformを含むHTMLが返され、JavaScriptでformを送信します。
おわりに
コマンドラインでも、SIWA使うことができました。これで、特定のAppleID持っている人じゃないと使えないコマンドラインツールでも作ることができます!本当に必要になるケースは極めて稀な気がしますが、もし必要になった人がいたら是非教えて下さい!
明日のMerpay Advent Calendar 執筆担当は、@Yhkwkmさんです。引き続きお楽しみください。