🐟

【Twitter API】OAuth2.0認証でAPIを使用してみる

2022/09/25に公開

概要

2021年の末頃に、Twitter APIのOAuth2.0認証がリリースされました。基本的に今後APIを使用する際はOAuth2.0認証を使用したほうが良いと思いますが、2022年9月時点で、そこまで情報等が多くないと感じたので、今回実装のサンプルを紹介します。

事前知識など

Twitter APIのOAuth2.0認証の概要については、下記の記事で概要がまとめられています。これらの記事の情報を元に実装していきます。
Twitter OAuth2.0の設定や動作まとめ
Twitter api v2でOAuth2.0認証でtweetする

実装方針

  • フロントエンド(Next.jsを使用)と、バックエンド(PHPを使用)に分けて実装します。
  • フロントエンドでは認可コードを取得するまで、バックエンドではアクセストークンの取得とユーザ情報の取得を行います。理由としてアクセストークンの取得には、TwitterのClientSecretの情報が必要になり、これをフロントに晒すのは避けたいためです。

実装サンプル

【フロントエンドの実装】

まずはTwitterの認証画面に、遷移させる箇所の実装です。
ランダム文字列の生成にはrandomstringというライブラリを使用しています。また、s256形式でのcode_challengeの生成はPKCEをJavaScriptで実装するの記事を参考にしています。
codeVerifierの値はTiwtterから戻って来た時に使いたいので、sessionStorageに保存しています。

AuthTwitterRegisterComponent.js
import randomstring from "randomstring";
import base64url from "base64url";
import jsSha256 from "js-sha256";

export default function AuthTwitterRegisterComponent() {
  async function onClickTwitterAuth() {
    const state = randomstring.generate(50);
    const codeVerifier = randomstring.generate(50);
    // リダイレクトで戻る時に使いたいのでsessionStorageに保存
    sessionStorage.setItem("codeVerifier", codeVerifier);

    const twitterOAuth2Url =
      `https://twitter.com/i/oauth2/authorize?` +
      `response_type=code&` +
      `client_id=${process.env.NEXT_PUBLIC_TWITTER_CLIENT_ID}&` +
      `redirect_uri=${location.protocol}//${location.host}/auth/twitter_redirect&` +
      `scope=tweet.read%20users.read&` +
      `state=${state}&` +
      `code_challenge=${base64url(jsSha256.arrayBuffer(codeVerifier))}&` +
      `code_challenge_method=s256`;
    window.location.href = twitterOAuth2Url;
  }

  return (
    <div className="w-64 flex justify-center items-center flex-col">
      <button
        className="w-64 bg-indigo-200 rounded px-4 py-2 border border-neutral-300"
        onClick={onClickTwitterAuth}
      >
        Twitterにログイン
      </button>
    </div>
  );
}

Twitterから戻ってきた後の実装です。
パラメータに認可コードが入ってるのでこれを取得するのと、sessionStorageに保存したcodeVerifierを取得してバックエンドに渡します。

twitter_redirect.js
import Router, { useRouter } from "next/router";

export default function TwitterRedirect() {
  const { query, isReady } = useRouter();

  useEffect(() => {
    if (isReady) {
      const codeVerifier = sessionStorage.getItem("codeVerifier");
      const { code } = query;
      // sessionStorageは削除する
      sessionStorage.removeItem("codeVerifier");
      if (codeVerifier && code) {
        // 認可コードとcodeVerifierをバックエンドに送信(記載は割愛)
	・
	・
	・
      } else {
        // 値が取れなかったらトップに遷移
        Router.push("/");
      }
    }
  }, [isReady]);

  return <></>;
}

【バックエンドの実装】

TwitterへのAPIはcurlを使って投げます。ログインユーザの情報はGET /2/users/meのAPIを使用します。
また、フロントエンドからパラメータを受け取る部分の実装は、記載割愛します。

TwitterAuthService.php
<?php

namespace App\Services\Auth;

class TwitterAuthService
{
    public function getUserInfoFromCode($authCode, $codeVerifier)
    {
        // access_tokenの取得
        $chGetToken = curl_init();
        curl_setopt($chGetToken, CURLOPT_POST, true);
        curl_setopt($chGetToken, CURLOPT_URL, 'https://api.twitter.com/2/oauth2/token');
        curl_setopt($chGetToken, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($chGetToken, CURLOPT_USERPWD, sprintf('%s:%s', env('TWITTER_CLIENT_ID'), env('TWITTER_CLIENT_SECRET')));
        curl_setopt($chGetToken, CURLOPT_POSTFIELDS, http_build_query(array(
            'code' => $authCode,
            'grant_type' => 'authorization_code',
            'client_id' => env('TWITTER_CLIENT_ID'),
            'redirect_uri' => env('FRONT_END_URL') . '/auth/twitter_redirect',
            'code_verifier' => $codeVerifier,
        )));
        $accessToken = json_decode(curl_exec($chGetToken))->access_token;

        // access_tokenを使ってログインユーザの情報を取得
        $chUserToken = curl_init();
        curl_setopt($chUserToken, CURLOPT_URL, 'https://api.twitter.com/2/users/me');
        curl_setopt($chUserToken, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($chUserToken, CURLOPT_HTTPHEADER, array('Content-Type: application/json', "Authorization: Bearer " . $accessToken));
        $userInfo = json_decode(curl_exec($chUserToken))->data;
        return $userInfo;
    }
}

その他

TwitterにAPIを投げる時Client Forbiddenというエラーが発生し少しハマったのですが、Twitter APIで"Client Forbidden"エラーの記事にある通り、Twitter Developer Potalでプロジェクトを作成し忘れたからでした。。

Discussion