Twitter Oauth2.0 認証のデモを作ってみた

この記事は SUPER STUDIO Advent Calendar 2022 17 日目の記事です。

初投稿になります。
株式会社 SUPER STUDIO でエンジニアをしております @uratakusan です。

ポケモン SV におけるトレーナー業務等の業務改善を行っていたら、記事を書くのが遅れました。

今回は、 Twitter Oauth2.0 認証機能のデモアプリを作りましたので、これをネタにして話せたらと思います。

デモアプリを作ったきっかけ

  • Twitter Oauth 2.0 Authentication が昨年末にリリースされたため、知見が欲しかったため

  • Twitter のクローリングを行うアプリを作ろうとしていたため

  • 数件サンプルを探したが、認証時に暗号化されていない場合が多かったため、より実践的なコードを書いてみたかったため

  • 誰かの圧力によってQiitaのアドベントカレンダー企画がなくなったため

Twitter Oauth 2.0 の認証処理について

  • Oauth 2.0 の説明については省略します。

  • 下記の遷移図に沿って、ログイン処理が進められます

    • アプリが Twitter の認証 URL を生成してリダイレクト

    • ユーザーが Twitter の認証 URL でアプリにアクセスを許可し、アプリのリダイレクト URL へリダイレクト

    • リダイレクト URL にて、 Twitter API から Access Token を取得

    • Access Token から Twitter の情報を取得

Twitter Oauth2.0 Authentication 遷移図

アプリの内容

  • Twitter にログインし、ログインしたユーザー情報を表示

Twitterログイン画面


表示するユーザーの情報

挙動確認方法

  • Twitter Developer Portal で V2 API を利用できる App を作成

    • すべて英語ですが、やり方が様々なサイトでまとまっているため省略

  • 対象のアプリのUser authentication settings にて下記を設定

  • GitHubからリポジトリを clone

  • .env.default をコピーして .envを作成

    • cp .env.default .env

  • .env に下記を設定

    • FLASK_SECRET_KEY : ランダム文字列

    • CLIENT_ID : Twitter Developer Portal で作成した AppのClient ID を追加

    • CLIENT_SECRET : Twitter Developer Portal で作成した App の Client Secret を .env に追加 

  • source .env で環境変数を適用

実装でつまずいた点

Twitter 認証画面のリダイレクトURL生成時に認証文字列を安全に保つには?

  • このサンプルを作る上で一番考えた点になります。

  • Twitter Oauth2.0 認証を安全に利用するには、 s256 で暗号化された文字列を認証時に利用する必要があります。(暗号化しないこともできますが、セキュアにやっていきましょう)

    • アプリが生成した Twitter 認証画面に s256 で暗号化した文字列 ( code_challenge ) をパラメーターに追加

    • Twitter 認証画面からリダイレクトしたアプリの URL にて access token をリクエストする際に復号化した文字列 ( code_verifier ) を Twitter に POST

    • Twitter のサーバー上で↑で渡された暗号化、復号化された文字列が一致した際に access token が取得される

  • まず、セッションに値をもたせることでユーザー毎に固有の値をランダム文字列を保管することはできました。

# index.py
oauth = TwitterOauth()
TokenCodeManager.create_code(session)
token_code = TokenCodeManager.fetch_code(session)
login_url = oauth.get_url(token_code)
return redirect(login_url)
from services.random_string_maker import RandomStringMaker

class TokenCodeManager:
    
    def create_code(session) -> None:
        # ランダム文字列を生成
        random_str = RandomStringMaker.exec()
        session['token_code'] = random_str
        
    def fetch_code(session) -> str:
        return session.get('token_code')
  • 続いて Twitter へのリダイレクト URL 生成時に暗号化をするロジックに苦労しました。

    • 単純に sha256 かければよいのではと余裕こいていた私は 2,3 日デバッグと調査に苦しみました。。。

    • Python で実装する方は真似していただけると幸いです

    • 実装したコード:services/twitter/auth.py

def __create_login_url_param_str(self, token_code: str) -> str:
    # sessionに保存されたランダム文字列の暗号化
    encoded_token_code = base64.b64encode(hashlib.sha256(token_code.encode('utf-8')).digest()).decode()
    code_challenge = encoded_token_code.rstrip('=').replace('=', '').replace('+', '-').replace('/', '_')
    
    # また別にランダムで文字列を渡す必要あり
    state = RandomStringMaker.exec(100)
    
    return f"response_type=code&client_id={self.CLIENT_ID}&redirect_uri={self.REDIRECT_URI}&scope=tweet.read users.read offline.access&state={state}&code_challenge={code_challenge}&code_challenge_method=s256"
  • 結構雑に書いたため、指摘いただけると幸いです。

感想

  • s256 での認証について、自分の知見がなかったため、時間がかかってしまった💦

  • 今まで認証周りの実装をあまりしたことがなかったので、今回の実装から自分の理解に進みました。

    • Twitter 以外での認証処理を今後実装する際にも活かせればと思います。

  • 実際にサービスを作ろうと思ってサンプルのために実装したが、まだそこまでできていない…というのを記事を書いていて思い出しました。

  • 冬休みは、ポケモンしながら開発も進めていければと

参考

この記事が気に入ったらサポートをしてみませんか?