【Flutter】【Firebase Auth】Twitterログイン失敗でハマった & Twitterログインの裏で何が行われているのか。

2021/12/05 22:21公開
Table of Contents
  1. ハマった。
  2. 初歩的な問題だった。
  3. おまけ
    1. 1. リクエストを作成する
    2. 2. 署名を作成する
    3. 3. APIを叩いてリクエストトークンを取得
    4. 4. ユーザーをログイン画面に移動させる
    5. 5. アクセストークンを取得する

ハマった。

FlutterのFirebase AuthenticationでTwitterログインを実装中、何度やっても下のようなエラーが出てログインできなかった。

The supplied auth credential is malformed or has expired. [ Failed to fetch resource from https://api.twitter.com/1.1/account/verify_credentials.json?include_email=true, http status: 401, http response: {"errors":[{"code":32,"message":"Could not authenticate you."}]} ]

これを解決するため、利用しているOAuthライブラリのバグかと思い、OAuthの仕組みから全部勉強してゼロから実装してみました が、直らず。

さらにAPIキーとシークレットの再生成も試したが、ダメ。

初歩的な問題だった。

初めてエラーが出て 2週間後、ふと思いつきました。

「あれ、Firebase Consoleで設定したAPIキー、更新してなくね」

ここでOAuthの仕組みの勉強が役に立ったわけですが、サインイン時にアクセストークンとシークレットを渡してsignInWithCredentialを呼ぶと、Twitterのverify_token APIが呼ばれている様子です。

なんとここには、アクセストークン・アクセストークンシークレットだけでなく、APIキー・APIシークレット もAPIを叩くのに必要でした。

つまり、TwitterLoginに渡すAPIキー・APIシークレットと同じものをFirebase Consoleに設定しないと、この段階でAPIキー・APIシークレットが間違っているためログインに失敗します。 つまり、クライアントとFBサーバーの2箇所で持たせる必要があります。

誰かのお役に立てば幸いです。

おまけ

ここからはOAuthの勉強で知った、Twitterログインの裏で何が行われているのかを説明してみます。

1. リクエストを作成する

まずはOAuthのリクエストトークンを取得するリクエスト内容を作成します。具体的には、HTTPメソッド、APIのエンドポイント(簡単に言えばクエリーパラメータを除いたURL)、クエリーパラメータを全部くっつけて1つの文字列にしたものです。

APIエンドポイントは「https://api.twitter.com/oauth/request_token」です。

必要なのは6つ。

①コールバックURI。サインイン後、どこに戻ってくるかのURL。

②APIキー

③ナンス(ランダムな文字列。生成方法は任意)

④署名方法「HMAC-SHA1」

⑤タイムスタンプ (現在時刻)

⑥OAuthバージョン「1.0」

クエリーパラメータはそれぞれの値をパーセントエンコードし、以下の例のように結合します。

oauth_callback=submon%3A%2F%2F&oauth_consumer_key={API_KEY}&oauth_nonce=abcdefg123456&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1638708010&oauth_version=1.0

※{API_KEY}の部分はAPIキー

次に、APIのエンドポイントとクエリーパラメータをそれぞれURLエンコード(パーセントエンコード)します。そして、それらを「&」を挟んでくっつけます。以下が例になります。これを①とします。

POST&https%3A%2F%2Fapi.twitter.com%2Foauth%2Frequest_token&oauth_callback%3Dsubmon%253A%252F%252F%26oauth_consumer_key%3D%7BCONSUMER_KEY%7D%26oauth_nonce%3Dabcdefg123456%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1638708010%26oauth_version%3D1.0

次に、APIシークレットとアクセストークンシークレットを「&」で結合した文字列を作成します。この段階でアクセストークンシークレットは存在しないため、空にします。以下が例になります。これを②とします。

{API_SECRET}&

※{API_SECRET}の部分はAPIシークレット。

2. 署名を作成する

②を鍵として、①をHMAC-SHA1でハッシュ化し、これをBase64エンコードします。これを③とします。

これはリクエスト内容が途中で改竄されていないかチェックする役割を果たしています。ハッシュ化されたものは基本的に復元できませんので、推測ですがTwitter側のAPIで同じ方法でハッシュを作り、一致するかチェックしていると思われます。

3. APIを叩いてリクエストトークンを取得

ようやくHTTPリクエストを作成します。先ほどのクエリーパラメータに oauth_signature=③ を追加してAPIを叩きます。

すると、ログイン画面用のトークン(④)とシークレットが返ってきます。

4. ユーザーをログイン画面に移動させる

次に、ユーザーを以下のURLにアクセスさせます。

https://api.twitter.com/oauth/authorize?oauth_token=④

すると、先程のトークンとベリファイア(?)(⑤)が返ってきます。

5. アクセストークンを取得する

最後に、アクセストークンを取得します。https://api.twitter.com/oauth/access_tokenに向かってoauth_consumer_key(APIキー)とoauth_token(④)とoauth_verifier(⑤)をぶつければアクセストークンとシークレットが返ってきます。これを使ってTwitterのユーザー情報にようやくアクセスできます。長かった。

以上となります。