こんにちは。プラットフォーム技術部第2グループの冷牟田です。
今回は、自社プロダクト開発を進める中でOAuth 2.0というものに触れました。いくつか実際に処理を実装したため、簡単にご紹介させていただきます。
OAuth 2.0について
OAuthとは
まず、読み方は「オーオース」です。
OAuthは「Open Authorization」の略で、この「Authorization」は認可を指します。
「認可」と似た言葉として「認証」がありますが、概念としては異なります。
「認証」はユーザの身元を確認するためのプロセスであり、「認可」はユーザがアクセスできるリソースや機能を制御するためのプロセスです。
通常、コンテンツ投稿やプロフィールの詳細情報を取得等をする時、リソースのアクセスに必要なオーナーのIDやパスワードを使用します。
しかし、OAuthでコンテンツ自動投稿機能を持った別サービスを認可してアクセストークンを生成することで、そのユーザの代わりにリソースにアクセスすることが出来るようになります。
これにより、パスワード等の個人情報を直接共有をせずに安全にリソースにアクセスする別のサービスにアクセス許可を与えることが出来るため、個人情報の保護の点でも安心です。
OAuth 1.0とOAuth 2.0の違い
OAuth認証には、OAuth 1.0とOAuth 2.0があります。
OAuth 1.0は最初の認可プロトコルであり、2007年にリリースされました。
その代替としてOAuth 2.0が設計され、よりシンプルで柔軟な認可プロトコルとして2012年にリリースされました。
現在は、OAuth 2.0が事実上の業界基準となっています。
OAuth 1.0ではリクエスト内容に署名を生成(HMAC-SHA1)して付与します。
そして、その署名によってリクエストの信頼性を保証します。
それに対して、OAuth 2.0ではHTTPSの使用を必須としたことで、リクエストの信頼性が保証されます。
加えて、OAuth 2.0のアクセストークンには有効期限もあるため、セキュリティの向上を図ることができ、仮に情報が漏れたとしてもリスクを最小限に抑えることが可能です。
OAuth 2.0の認可フロー
OAuth 2.0にはいくつかの認可フローがありますが、今回は実際に実装した最も基本的なフローである「Authorization Code フロー」について説明します。
① 認可コード発行リクエストを実行
画面から認可コード発行用のリクエストを実行して、対象の認可サーバにリダイレクトします。
そこで、リクエストで指定したスコープ(リソースや操作のアクセス権限を付与する範囲の設定)を確認して承認します。
承認すると認可サーバーは認可コードを発行して、リクエストで指定したリダイレクトURIにリダイレクトされます。
② 認可コードを取得
リダイレクトされた時に、URIのクエリに認可コードが含まれているため、それを取得します。
③ アクセストークン発行リクエストを実行
発行された認可コードを使ってアクセストークン取得用のリクエストを実行した時、情報が正しければ、アクセストークンが返ります。
この時、アクセストークンの有効期限が切れて使用不可になる前に更新するためのリフレッシュトークンも含めるように、パラメータで設定することもできます。
※ 上の認可フロー図に記載しているクライアントは、実際にリソースにアクセスしようとするサービスを指しており、YouTubeに自動投稿できる機能を持ったサービス等のことです。
プラットフォームによるOAuth 2.0での実装の違い
今回、YouTube, X(旧Twitter), Instagramの3つに絞って、それぞれの仕様に合ったOAuth 2.0の実装をしてみました。
この時、必要なパラメータやレスポンス内容等が若干異なっていたため、違いを以下にまとめてみました。
YouTubeのOAuth 2.0
事前準備
① Google Cloud Platformにログインして、プロジェクトを作成します。
②「プロダクト -> APIとサービス -> ライブラリ」の画面で、「YouTube Data API v3」を有効にします。
③「プロダクト -> APIとサービス -> OAuth同意画面」の画面で、アプリを作成します。
④「プロダクト -> APIとサービス -> 認証情報」の画面で、「認証情報を作成」をクリックして、「OAuthクライアントID 」を選択します。
⑤ 実装で使うリダイレクトURIを「承認済みのリダイレクトURI」に設定して、作成します。
上記を行うことで、クライアントIDとクライアントシークレットが発行されたクライアントができます。
実装
① 認可コード発行用のリクエストを実行
以下のURIにリダイレクトします(読みやすさのために改行しています)。
https://accounts.google.com/o/oauth2/auth ?client_id={クライアントID} &redirect_uri={リダイレクトURI} &scope={スコープ} &response_type=code &access_type=offline &state={ステート}
必須/推奨パラメータ
client_id | 事前準備で用意されたクライアントID | 必須 |
---|---|---|
redirect_uri | 事前準備の⑤で指定したURI | 必須 |
scope | アクセス制限(複数指定する場合は半角スペース区切り) | 必須 |
response_type | "code"で固定 | 必須 |
access_type | リフレッシュトークンが必要であれば"offline"を指定 | 必須 |
state | CSRF対策用のランダムな文字列 | 推奨 |
用意されているscope一覧は以下の公式サイトを参照してください。
developers.google.com
パラメータ情報を詳しく知りたい場合は以下の公式サイトを参照してください。developers.google.com
② 認可コードを取得
認可後、リダイレクトされたURIは下記のようになります。
Ex.
https://example.com ?scope=email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyoutube.upload &code=EXAMPLE_CODE
③ アクセストークン取得用のリクエストを実行
以下を実行します。
curl -XGET 'https://oauth2.googleapis.com/token' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'client_id={クライアントID}' \ --data-urlencode 'client_secret={クライアントシークレット}' \ --data-urlencode 'redirect_uri={リダイレクトURI}' \ --data-urlencode 'code={認可コード}' \ --data-urlencode 'grant_type=authorization_code'
必須パラメータ
client_id | 事前準備で用意されたクライアントID |
---|---|
client_secret | 事前準備で用意されたクライアントシークレット |
redirect_uri | 認可コード発行時に利用したリダイレクトURI |
code | ②で取得した認可コード |
grant_type | "authorization_code"で固定 |
④ アクセストークンを取得して保存
以下のようなレスポンスが返るため、このデータをDB等に保存します。
{ "access_token": "<example-access-token>", "expires_in": 3600, // 1時間 "refresh_token": "<example-refresh-token>", "scope": "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/youtube.upload", "token_type": "Bearer" }
動作確認
scopeに"https://www.googleapis.com/auth/userinfo.email"を指定して取得したアクセストークンを使って、以下のユーザ情報取得用のリクエストを実行してます。
curl -XGET 'https://www.googleapis.com/oauth2/v1/userinfo' \ --header 'Authorization: Bearer {アクセストークン}'
この時、以下のようなレスポンスが返ってくれば正常です。
{ "id": "hoge", "email": "hoge@example.com", "verified_email": true, "picture": "https://example.com/hoge", "hd": "example.com" }
X(旧Twitter)のOAuth 2.0
今回のX(旧Twitter)についての説明は、「Confidential Client」というバックエンド側でクレデンシャル情報を安全に保持できるクライアントの使用を前提としています。
事前準備
① X Developer Potalにログインして、アプリ詳細画面を開きます。
②「Settings -> User authentication settings」の「Set Up」ボタンをクリックします。
③ 実装で使うリダイレクトURIを「App info -> Callback URI / Redirect URL」に設定して、保存します。
上記を行うことで、クライアントIDとクライアントシークレットが発行されて、「Keys and tokens -> OAuth 2.0 Client ID and Client Secret」にも表示されます。
実装
① 認可コード発行用のリクエストを実行
以下のURIにリダイレクトします(読みやすさのために改行しています)。
https://twitter.com/i/oauth2/authorize ?client_id={クライアントID} &redirect_uri={リダイレクトURI} &scope={スコープ} &response_type=code &state={ステート} &code_challenge={コードチャレンジ} &code_challenge_method=plain
必須/推奨パラメータ
client_id | 事前準備で用意されたクライアントID | 必須 |
---|---|---|
redirect_uri | 事前準備の⑤で指定したURI | 必須 |
scope | アクセス制限(複数指定する場合は半角スペース区切り) リフレッシュトークンが必要であれば"offline.access"が必要。 |
必須 |
response_type | "code"で固定 | 必須 |
state | CSRF対策用のランダムな文字列 | 必須 |
code_challenge_method | "plain" or "S256" | 必須 |
code_challenge | code_challenge_methodが"plain"の時はcode_verifierそのもの、"S256"の時はcode_verifierのSHA-256ハッシュ値をセーフなbase64URLエンコードしたもの。 | 必須 |
※ code_verifierは、43文字〜128文字の [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~" からなるランダムな文字列。code_challengeやcode_challenge_methodは、認可コード横取り攻撃への対策を目的として策定されたPKCEという仕様に基づくパラメータです。
用意されているscope一覧は以下の公式サイトを参照してください。developer.twitter.com
パラメータ情報を詳しく知りたい場合は以下の公式サイトを参照してください。
developer.twitter.com
② 認可コードを取得
認可後、リダイレクトされたURIは下記のようになります。
Ex.
https://example.com?code=EXAMPLE_CODE&state={ステート}
この時、リダイレクトされたURIのクエリのstateの値が、①で指定したstateの値と同じであるかを確認してください。
③ アクセストークン取得用のリクエストを実行
以下を実行します。
curl -XGET 'https://api.twitter.com/2/oauth2/token' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --header 'Authorization: Basic {「[クライアントID]:[クライアントシークレット]」をBase64エンコードしたもの}' \ --data-urlencode 'redirect_uri={リダイレクトURI}' \ --data-urlencode 'code={認可コード}' \ --data-urlencode 'code_verifier={コードベリファイア}' \ --data-urlencode 'grant_type=authorization_code'
必須パラメータ
redirect_uri | 認可コード発行時に利用したリダイレクトURI |
---|---|
code | ②で取得した認可コード |
code_verifier | ①で使用したcode_verifier |
grant_type | "authorization_code"で固定 |
④ アクセストークンを取得して保存
以下のようなレスポンスが返るため、このデータをDB等に保存します。
{ "access_token": "<example-access-token>", "expires_in": 7200, // 2時間 "refresh_token": "<example-refresh-token>", "scope": "users.read tweet.write", "token_type": "Bearer" }
動作確認
scopeに"users.read"を指定して取得したアクセストークンを使って、以下のユーザ情報取得用のリクエストを実行します。
curl -XGET 'https://api.twitter.com/2/users/me' \ --header 'Authorization: Bearer {アクセストークン}'
この時、以下のようなレスポンスが返ってくれば正常です。
{ "data": { "id": "hoge", "name": "hoge", "username": "hoge" } }
注意点
YouTubeと違い、"code_challenge"、"code_challenge_method"、"state"のパラメータが必須となります。
InstagramのOAuth 2.0
事前準備
① Meta for Developersにログインして、アプリを作成します。
※ アプリタイプはビジネスタイプである必要があります。
②「ダッシュボード -> アプリに製品を追加 -> ビジネス向けFacebookログイン」の設定ボタンをクリックします。
③ 実装で使うリダイレクトURIを「クライアントOAuth設定 -> 有効なOAuthリダイレクトURI」に設定して、保存します。
実装では、「ダッシュボード -> ベーシック」画面の「アプリID」をclientId、「app secret」をclientSecretとして対応します。
実装
① 認可コード発行用のリクエストを実行
以下のURIにリダイレクトします(読みやすさのために改行しています)。
https://www.facebook.com/v19.0/dialog/oauth ?client_id={クライアントID} &redirect_uri={リダイレクトURI} &scope={スコープ} &response_type=code &display=group &auth_type=rerequest &state={ステート}
必須/推奨パラメータ
client_id | 事前準備で用意されたクライアントID | 必須 |
---|---|---|
redirect_uri | 事前準備の⑤で指定したURI | 必須 |
scope | アクセス制限(複数指定する場合は半角スペース区切り) | 必須 |
response_type | "code"で固定 | 必須 |
display | "group"で固定 | 必須 |
auth_type | "rerequest"で固定 | 必須 |
state | CSRF対策用のランダムな文字列 | 推奨 |
用意されているscope一覧は以下の公式サイトを参照してください。developers.facebook.com
パラメータ情報を詳しく知りたい場合は以下の公式サイトを参照してください。
developers.facebook.com
② 認可コードを取得
認可後、リダイレクトされたURIは下記のようになります。
Ex.
https://example.com?code=EXAMPLE_CODE
③ アクセストークン取得用のリクエストを実行
以下を実行します。
curl -XGET 'https://graph.facebook.com/v19.0/oauth/access_token' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'client_id={クライアントID}' \ --data-urlencode 'client_secret={クライアントシークレット}' \ --data-urlencode 'redirect_uri={リダイレクトURI}' \ --data-urlencode 'code={認可コード}
必須パラメータ
client_id | 事前準備で用意されたクライアントID |
---|---|
client_secret | 事前準備で用意されたクライアントシークレット |
redirect_uri | 認可コード発行時に利用したリダイレクトURI |
code | ②で取得した認可コード |
④ アクセストークンを取得して保存
以下のようなレスポンスが返るため、このデータをDB等に保存します。
{ "access_token": "<example-access-token>", "expires_in": 3600, // 1時間 "scope": "email instagram_content_publish", "token_type": "bearer" }
動作確認
scopeに"email"を指定して取得したアクセストークンを使って、以下のユーザ情報取得用のリクエストを実行します。
curl -XGET 'https://graph.facebook.com/v19.0/me?access_token={アクセストークン}&fields=email'
この時、以下のようなレスポンスが返ってくれば正常です。
{ "email": "hoge@example.com", "id": "hoge" }
注意点
YouTubeやX(旧Twitter)と違い、"auth_type"等のパラメータが必要となります。
また、それぞれのパスにはバージョンを指定する必要があります。
このバージョンは約2年程で有効期限が切れることもあるため、定期的に確認する必要があります。
まとめ
以上、3つのプラットフォーム用の仕様で実装してみた時、使い方に少しずつ違いがあることが分かりました。
今回、アクセストークンの取得までをご紹介しましたが、このアクセストークンを使ってリソースにアクセスする時、利用プランによってレート制限が変わることもあるため、事前に調べて対応することをお勧めします。
今後OAuth 2.0を触ることがあれば、調査に時間を使わないようお役に立てると幸いです。
最後まで読んでいただきありがとうございました。