🚦

GoでTwitter APIのOAuth 2.0 Authorization Code Flow with PKCEを叩く

2022/05/03に公開

概要

Twitter APIのOAuth 2.0 Authorization Code Flow with PKCEをcode_verifierとcode_challengeを生成して叩いている記事が見つからなかったので、実際に生成しつつTwitterクライアントライブラリで叩いてみました。

なお、この記事ではクライアントという単語はOAuth2.0クライアントのことを指します。

OAuth 2.0 Authorization Code Flow with PKCE

OAuth 2.0 Authorization Code Flow with PKCEはTwitter APIの認証方法の一つです。ツイートの検索などログインしなくてもできる処理はクライアントのみ(OAuth 2.0 Bearer Token (app-only))で実行できます。

一方でアカウントのフォローやリツイートなどの操作は「誰が行ったか」の情報が必要です。この場合、リツイートなどの操作を実行するアカウントによってクライアントを認証する機能なければ、クライアントが操作を行うことはできません。

そこでOAuth 2.0 Authorization Code Flow with PKCEを利用します。OAuth 2.0 Authorization Code Flow with PKCEはAuthorization Code FlowをPKCEで拡張した方式です。Twitterでは以下のように定義されています。

An extension to the authorization code flow to prevent several attacks and to be able to perform the OAuth exchange from public clients securely.

Authorization Code Flow(認可コードフロー)はOAuth2.0の認可フローの一つです。Client Secretを使い、認証の機能を持ちます。ここでは詳しいフローを書きませんが、詳しくはAuthleteの川崎さんの記事などが参考になります。

PKCEpixyと発音するらしい)とはProof Key for Code Exchangeの略で、Public Clientを認可コード横取り攻撃から認可コードフローを保護する拡張です。詳しい仕様は

https://www.authlete.com/ja/developers/pkce/

のサイトが参考になりました。

Public Client

今回はPublic Clientではなく、Confidential Clientを使用しています。二つの大きな違いはClient Secretを使えるかどうかですが、今回はあまり意識する必要がありません。

この拡張は以下のパラメータを新しく定義します。

code_verifier

43文字〜128文字のURLセーフな文字列

code_challenge_method

S256plainの文字列

code_challenge

code_challenge_method=plainの場合はcode_verifierそのもの。
code_challenge_method=S256の場合はcode_verifierのsha256によるハッシュ値をbase64 urlエンコードしたもの。

codeVerifiercodeChallengeの使用方法についてざっくり述べると

  1. クライアントは認可サーバーの認可エンドポイントへのリクエストの際、code_challengeをクエリストリングに含める。
  2. サーバーはクエリストリングからcode_challengeを取得し、データベースに保存する。
  3. クライアントは認可サーバーのトークンエンドポイントへのリクエストの際、code_verifierをリクエストボディに含めて送信する。
  4. サーバーはcode_verifierからcode_challengeを計算し、2でデータベースに保存しておいたcode_challengeと比較して等しければクライアントのリクエストを受理する。

code_challenge_methodS256が推奨されます。なぜなら、code_challengeはクエリストリングとして利用される値であり、暗号化されることなく通信路を流れるため、code_challenge=code_verifierだと、簡単に盗聴されてしまいます。

前準備

このコードを実行するためにはTwitter Developer PortalTwitter側で設定をし、

  • Redirect URI
  • Client Secret
  • Client ID

のパラメータを指定する必要があります。この指定については以下の記事がとても参考になりました。
https://blog.p1ass.com/posts/twitter-api-oauth-2/

なお、今回のソースではRedirect URIはhttp://localhost:3000/callbackであると仮定します。

パラメータの生成

今回使用するコードのソースです。

https://github.com/senk8/go-twitter-oauth

今回はcode_challenge_methodS256に固定します。 つまり、code_challengecode_verifierのハッシュ値になります。3つのパラメータを生成しているのはソースコードのうち、以下の部分です。

https://github.com/senk8/go-twitter-oauth/blob/master/oauth2/session.go

stateは認可コードフローで認可コードフローで攻撃を検知するためのパラメータです。詳しくはこちらなどを参考になりました。

それではPKCEの各パラメータの生成方法を確認します。code_verifierはRFCでは

NOTE: The code verifier SHOULD have enough entropy to make it
impractical to guess the value. It is RECOMMENDED that the output of
a suitable random number generator be used to create a 32-octet
sequence. The octet sequence is then base64url-encoded to produce a
43-octet URL safe string to use as the code verifier.

とあるので、これに倣って32オクテット(バイト)の乱数を生成してbase64 URLエンコードすることで生成しています。そして、codeChallengecodeChallengeMethodに従い、sha256でハッシュ値のバイト列にして、それをbase64URLエンコーディングしています。

実際に3つのパラメータを利用しているのはソースの以下の部分です。

https://github.com/senk8/go-twitter-oauth/blob/master/oauth2/config.go

buildAuthzURL、つまり認可エンドポイントへのリクエスト時にcode_challengeを含め、buildTokenRequest、つまりトークンエンドポイントへのリクエスト時にcode_verifierを含めています。

テスト

twitter v2のクライアントライブラリとして以下を利用します。

https://github.com/sivchari/gotwtr

環境変数の読み取りには以下を参考に、godotenvを使いました。

https://zenn.dev/a_ichi1/articles/c9f3870350c5e2

叩くTwitter v2 APIはmeがまだgotwtrでは実装されていないのでTweets Lookupを使います。

では、試しに自分の以下のツイートを取得してみます。実行方法についてはREADMEを参考にしてください。

https://twitter.com/senkRust/status/1493108554618015752

https://github.com/senk8/go-twitter-oauth/blob/master/main.go

❯ go run main.go
{1493108554618015752 俺、修論終わったら本気出すんだ <nil>  []   <nil> <nil>   <nil> <nil> false <nil> <nil> []   <nil>}

取得できました。他のAPIを叩きたい場合、必要なスコープが違うので、各APIのOAuth 2.0 scopes required by this endpointを参考にして、28行目のScopesを書き換える必要があります。

まとめ

OAuth 2.0 Bearer Token (app-only)だとベアラートークンを投げつけるだけなのですが、OAuth 2.0 Authorization Code Flow with PKCEを使うとなると結構難しいですね、、

間違いや質問等あったらコメントください!

参考にした記事

https://qiita.com/TakahikoKawasaki/items/200951e5b5929f840a1f
https://datatracker.ietf.org/doc/html/rfc7636
https://www.authlete.com/ja/developers/pkce/
https://zenn.dev/sivchari/articles/2b55ebfdb34621

Discussion