概要
- Using OAuth 2.0 to Access Google APIsにシーケンスがかいてあります。
- 上記のシーケンスはシンプルすぎて、重要なシーケンスとアクターが漏れているので、もう少し細かくシーケンス図を書き直しました。
- 特に、
refresh token
の取得方法と、取得タイミング、保存タイミングなどの情報がアプリケーション開発者がアプリケーションを設計する上で重要なのですが、オフィシャルドキュメントでは網羅されていませんでした。
シーケンス図
用語の整理
authorization code
- 認証コード
- これを元に、
refresh token
とaccess token
を交換する。取得したらauthorization code
は無効になる。 - ユーザが承認した直後にしか使わない。
- expireする。Googleでは時間の記載は無いけど、Facebookでは10分。
- 4番のシーケンスにおける、
code
パラメータで取得出来る。。
access token
- API呼び出しに使うトークン。
- レスポンスを見ていると、Googleでは3600秒(=60分)でexpireするトークンが発行されている。
refresh token
- 基本的には、アプリケーション側で永続化しておく。
- expireはしない。ユーザ側でrevokeできる。
- 無効になってしまったら
authorization code
から生成する必要がある。そのために、ユーザに再び承認をもらう必要がある。この時点ではauthorization code
は存在していないはずなので。 - 大事に保存しておく必要がある。このcredentialとアプリ情報が第三者に漏れると、権限を乱用出来てしまう。
- 漏れた場合は、Googleに登録したアプリをのcredentialをリセットする必要があるので、他のユーザにも影響するため厳重に管理する必要がある。
メモ
- expirationやトークンのライフサイクルに関してはこの辺りが情報ソース。
- authorization tokenを再発行するためには、自分のGoogleのSecurity&Privacyページに手アプリの認証をrevokeする必要がある。
- そうじゃないと、新しいaccess_tokenが発行されるだけで、認証情報はリセットされない。
- アプリ側で生成する認証ページのURLのパラメータを変更したときには、revokeしなければ、前回と同じパラメータの認証情報のままaccess tokenなどが生成されてしまうので注意。
ソースコード
- rubyのコードで主要なコードスニペットを書いておきます。
- わかりやすいように値の例も書いておきます。
refresh token
を取得できる、authorization code
を取得するコード。
client = Signet::OAuth2::Client.new({ client_id: ENV.fetch('GOOGLE_API_CLIENT_ID'), client_secret: ENV.fetch('GOOGLE_API_CLIENT_SECRET'), authorization_uri: 'https://accounts.google.com/o/oauth2/auth', scope: [Google::Apis::GmailV1::AUTH_GMAIL_READONLY, 'email'], redirect_uri: 'http://localhost:3000/callback', access_type: 'offline', # need this to get refresh_token. }) # ex: # http://localhost:3000/callback?code=4/nQCxXval2Q8tfg0StBx81vcILZU7kEp9BMkGq27geFsSJU3fOQ8c_dMz5VW_hySmXVq0BWtJ_lopkUk66u8ZoCx&scope=openid+email+https://www.googleapis.com/auth/plus.me+https://www.googleapis.com/auth/gmail.readonly+https://www.googleapis.com/auth/userinfo.email client.authorization_uri.to_s
authorization code
からrefresh token
とaccess token
を取得するコード。
client = Signet::OAuth2::Client.new({ client_id: ENV.fetch('GOOGLE_API_CLIENT_ID'), client_secret: ENV.fetch('GOOGLE_API_CLIENT_SECRET'), token_credential_uri: 'https://oauth2.googleapis.com/token', }) client.code = "[authorization code]" access_token_hash = client.fetch_access_token! # ex: # ya29.GltcBh5BsH71zSz3HI_NCTTyn2W5gfn3eVUXoQU3sHkMxF-Tu3x1gL3mZSNFM_HIglSz8oKiVj-vgFB1td-UzdXaKbutynonpws-dsG4l1mnudwFiZv9Dku8Hb-X access_token_hash['access_token'] # ex: # 1/L8qZrV5S88vOmRd9WVWxAmv4c8QJB9lMf9mqnfkZkVk access_token_hash['refresh_token']
refresh token
を使って、access token
を取得する。
client = Signet::OAuth2::Client.new({ client_id: ENV.fetch('GOOGLE_API_CLIENT_ID'), client_secret: ENV.fetch('GOOGLE_API_CLIENT_SECRET'), token_credential_uri: 'https://oauth2.googleapis.com/token', }) client.refresh_token = '[refresh_token]' access_token_hash = client.fetch_access_token! # ex: # ya29.GltcBhsO9iH4fWJITG3chjNrki5vk0M1I-bFcjTsHW2Ln4CBRZjT-eGBSlf_Qa4MewfWxsrU_s-DMb4AwOrkFpgUuGegStiGKKZkiazoPU5c3JUihNzOTcdJeKKX access_token_hash['access_token']
Comments