ふんわり始めるX API認証|OAuth2.0とPKCEを初心者向けに図解解説

ふんわり始めるX API認証|OAuth2.0とPKCEを初心者向けに図解解説

目次

📌 この記事について

対象読者: OAuth2.0という名前は聞いたことがあるけど、具体的にどうすればよいかわからない。でも、X API経由で投稿してみたい人

ゴール: OAuth2.0とPKCEの概念を理解して、実装に入る準備ができる

X API OAuth2.0とPKCEの仕組みをラーメン屋の比喩でふんわり解説。認証コードフローからcode_verifierの安全な生成方法、CSRF対策まで初心者にもわかりやすく図解で紹介。実装前の概念理解に最適


はじめに

ども!X APIを活用したアプリ開発をしている龍ちゃんです。最近、新卒エンジニア向けに技術研修の資料を作っていたんですが、やっぱり認証周りってちゃんと整理しないとダメだなと痛感しています。

この記事では、OAuth2.0について特に詳しくない方でも、X APIを実装できるようになるための基礎知識を解説しますね。ちゃんと理解したほうが良い概念ではありますが、まずは「ふんわり理解」から始めましょう。

しっかり勉強したい方は、以下の2冊がおすすめです:

弊社のブログであれば合わせて読むなら以下の連載がお勧めです!

  1. 【連載】世界一わかりみの深いOAuth入門 〜 その1:OAuthってなに? 〜
  2. 【連載】世界一わかりみの深いOAuth入門 〜 その2:アクセストークンとリフレッシュトークン 〜
  3. 【連載】世界一わかりみの深いOAuth入門 〜 その3:OAuthを認証に使うことの危険性 〜
  4. 【連載】世界一わかりみの深いOAuth入門 〜 その4:stateパラメーターによCSRF対策 〜

こちらの内容を活用して作成したアプリの記録は「AIチャットで話すだけ!X予約投稿を完全自動化するシステム構築術
」で解説しています。


なぜOAuth2.0が必要なの?

X APIを使ってツイートを投稿したり、情報を取得したりするには、「このアプリは信頼できる」「このユーザーが許可している」ことを証明する必要があります

昔ながらの方法(ID/パスワード直接入力)の問題点

もしID/パスワードをアプリに直接入力する方式だと:

  • ❌ アプリがパスワードを保存できてしまう
  • ❌ パスワードが漏洩したら全てのサービスで不正利用される
  • ❌ アプリに全権限を渡すことになる(投稿だけしたいのに、DM読み取りもできる)

OAuth2.0を使うと

  • ✅ パスワードを教えずに権限だけ渡せる
  • ✅ 必要な権限だけを限定できる(投稿のみ、読み取りのみなど)
  • ✅ いつでも権限を取り消せる

まずは、X APIを使用するために必要な基本的な画面を見てみましょう。

X API OAuth2.0の認証画面 - SIOSTechLabアプリへのアクセス許可を求める画面のスクリーンショット

X API OAuth2.0の認証画面 – SIOSTechLabアプリへのアクセス許可を求める画面のスクリーンショット

このような画面を表示させてアカウント連携を行い、トークン(アクセストークン・リフレッシュトークン)を取得します。そのトークンを使用してX APIを実行するわけですね。


🍜 OAuth2.0をラーメン屋で例えると

OAuth2.0の仕組みを理解するために、ラーメン屋で例えてみましょう。

登場人物

  • あなた: APIを使いたいユーザー(Resource Owner)
  • アプリ: あなたが使うアプリケーション(Client)
  • 券売機: X の認証システム(認可サーバー / Authorization Server)
  • ラーメン屋: X APIサービス本体(リソースサーバー / Resource Server)

全体の流れ

あなたは人気のラーメン屋(X)でラーメン(APIリソース)を食べたいです。でも、このラーメン屋は直接注文できません。専用アプリ経由でしか注文できないルールなんですね。

ステップ1: アプリで注文開始

「このアプリでラーメン食べたい!」とアプリに伝えます。

ステップ2: 券売機で申し込み(認可リクエスト)

アプリが券売機(認可サーバー)に「このラーメンを注文したいです」と申請書を出します。

ステップ3: 本人確認(ログイン・認可)

券売機が「あなた本人ですか?」と確認します。顔認証(Xへのログイン)で本人確認を済ませますね。

ステップ4: 引換券をもらう(コールバック)

本人確認が完了すると、券売機から**一時的な引換券(認可コード: code)**をもらいます。

ステップ5: 引換券→食券に交換(トークンリクエスト)

引換券を持ってアプリが窓口に行き、**本物の食券(アクセストークン)**に交換します。

ステップ6: ラーメンゲット!(リソースアクセス)

食券を渡してラーメン(APIデータ)を受け取ります。

なぜこんなに面倒なの?

もしID/パスワードを直接アプリに教える方式だと、悪意のあるアプリがパスワードを盗んで好き放題できてしまいますよね。

OAuth2.0なら:

  • 「このアプリにラーメン注文の権限だけあげる」という権限の制限ができる
  • パスワードをアプリに教えなくていい
  • いつでも権限を取り消せる(食券を無効化できる)

必要なエンドポイント

この仕組みを実装するには、最小で2つのエンドポイントが必要です:

1. 認証URL発行エンドポイント

券売機への申請書を出すためのエンドポイントですね。ユーザーをX の認証画面にリダイレクトします。

2. コールバックエンドポイント

券売機から発行された引換券(code)を受け取るエンドポイント。このcodeを使ってアクセストークンを取得します。


OAuth2.0の詳細な流れ(シーケンス図)

OAuth2.0認可コードフローのシーケンス図 – ユーザー、クライアントアプリ、認可サーバー、リソースサーバー間の通信フローを図解

各ステップの詳細

ステップざっくり理解正確な流れ
2. 認可リクエスト認可リクエストのURLをください認可の開始: クライアントがユーザーを認可サーバー(AS)へ誘導し、「この**権限(Scope)**を借りたい」とASに伝える
3〜4. ログイン・認可その用途で使用することに合意します。本人確認は行いました権限の付与: ユーザーがAS上でログインし、クライアントアプリが要求した権限(Scope)の付与を許可する
5. コールバック本人確認了承しました。きちんと処理が行われていますね認可コードの受け渡し: ASがクライアントアプリのコールバックURLに認可コード(code)とstateを渡す。これは一時的で、まだサービスにアクセスするためのキーではない
6〜9. トークンリクエスト具体的にサービスにアクセスするためにキーの取得を行いますねトークンへの交換: クライアントがASに対し、受け取ったcodeを提示して、サービスアクセス用の本物のキーであるアクセストークンとの交換を要求する
10. リソースリクエストキーを使ってサービスにアクセスを行いますリソースへのアクセス: 取得したアクセストークンを提示して、リソースサーバー(RS)に保護された情報(リソース)へのアクセスを要求する

X APIで必要な重要パラメータ

1. Scope(スコープ): 「何ができる権限か」

ラーメンの例で言うと「ラーメンだけ注文できる券」なのか「ラーメン+餃子まで注文できる券」なのか、という権限の範囲ですね。

X APIでよく使うScope

Scope説明
tweet.readツイートを読む権限
tweet.writeツイートを投稿する権限
users.readユーザー情報を読む権限
follows.readフォロー情報を読む権限
follows.writeフォロー/アンフォローする権限

重要: 必要な権限だけを要求するのがセキュリティ上重要です。投稿だけしたいのに全権限を要求すると、ユーザーに警戒されちゃいますよね。

2. redirect_uri(リダイレクトURI): 「引換券を受け取る住所」

認可サーバーが「引換券(code)をどこに送ればいいの?」を指定するのがredirect_uriです。

重要なポイント

  • X Developer Portalで事前に登録したURLと完全一致する必要がある
  • 末尾の / まで含めて一致させる
  • 本番環境: https://yourdomain.com/callback
  • 開発環境: http://localhost:3000/callback (ローカル開発用)

よくあるエラー

❌ Developer Portalに登録: <https://example.com/callback>
❌ コードで指定: <https://example.com/callback/>
→ 末尾のスラッシュが違うのでエラー!

✅ Developer Portalに登録: <https://example.com/callback>
✅ コードで指定: <https://example.com/callback>
→ 完全一致でOK!

3. state: 「偽造防止のおまじない」

CSRF攻撃を防ぐためのランダムな文字列です。認可リクエストで送ったstateと、コールバックで返ってきたstateが一致するか確認します。

ラーメンの例で言うと「整理番号」のようなものですね。自分の整理番号と違う引換券が返ってきたら怪しいですよね。

// state生成の例
const state = crypto.randomUUID(); // "550e8400-e29b-41d4-a716-446655440000"
sessionStorage.setItem('oauth_state', state);

// コールバックで確認
const returnedState = new URLSearchParams(window.location.search).get('state');
const savedState = sessionStorage.getItem('oauth_state');

if (returnedState !== savedState) {
  throw new Error('State不一致!CSRF攻撃の可能性あり');
}

PKCEが必要な理由

さて、ここまでの仕組みでも一応動きますが、実はセキュリティリスクがあります。

🚨 認可コード(code)が盗まれたら?

もし通信途中でcodeが盗聴・盗難された場合、以下のような攻撃が可能になります:

OAuth2.0認可コードフローのシーケンス図 - ユーザー、クライアントアプリ、認可サーバー、リソースサーバー間の通信フローを図解

OAuth2.0認可コード横取り攻撃のシーケンス図 – PKCEなしの場合に攻撃者が認可コードを盗聴するリスクを図解

具体的には:

  1. 正規のクライアントアプリで認可リクエストを送る
  2. 通信途中で攻撃者がcodeを盗聴
  3. 攻撃者が盗んだcodeを使ってトークンリクエストを送る
  4. 認可サーバーは正しいcodeなのでトークンを発行してしまう
  5. 攻撃者がユーザーのリソースにアクセスできてしまう

ラーメン屋で例えると

PKCEなしの場合(危険!):

  • 券売機で引換券だけもらう
  • 引換券を持って窓口に行けば食券がもらえる
  • → 途中で引換券を盗まれたら、誰でも食券に交換できちゃう!

PKCEありの場合(安全!):

  1. 券売機で注文する時に「愛言葉」を決める
  2. 券売機には「愛言葉のヒント」だけ伝える
  3. 引換券をもらう
  4. 窓口で引換券と「愛言葉の本物」を見せる
  5. 店員が「ヒントと愛言葉が一致するか」確認してから食券を渡す

→ 引換券を途中で盗まれても、愛言葉がないと食券には交換できない!


PKCEの仕組み

PKCE(Proof Key for Code Exchange)は、認可リクエストしたクライアントとトークンリクエストを行ってきたクライアントが同一であることを証明するための仕組みです。

PKCEありOAuth2.0認証のシーケンス図

主要な登場人物

用語説明ラーメンの例
code_verifierあなただけが知っている秘密の文字列愛言葉の本物
code_challengecode_verifierをハッシュ化したもの愛言葉のヒント

PKCEの流れ

  1. 事前準備: クライアントアプリがcode_verifier(秘密の文字列)を生成
  2. 認可リクエスト: code_challenge(ハッシュ化したもの)を送信
  3. コールバック: codeを受け取る
  4. トークンリクエスト: codeと一緒にcode_verifier(元の文字列)を送信
  5. 検証: 認可サーバーがcode_verifierをハッシュ化して、最初のcode_challengeと一致するか確認

code_verifierのよくある誤解

危険な実装:固定の愛言葉を使い回す

code_verifierを固定文字列や予測可能な値にしてしまうのは、とても危険な実装です。

// ❌ 危険な例:固定文字列
const code_verifier = "my_secret_verifier_2024";

これでは、PKCEの意味がなくなってしまいます。

ラーメン屋で例えると

悪い例:

  • 店員:「愛言葉は?」
  • あなた:「いつも『ラーメン大好き』って決めてるんだ!」
  • 盗聴者:「次回も『ラーメン大好き』って言えば食券もらえるな…」

良い例:

  • 店員:「愛言葉は?」
  • あなた:「今回は『X7mK9pQ2vR8nL3zA』!」(毎回ランダムに自動生成)
  • 盗聴者:「今回の愛言葉聞いたけど、次は全然違うやつ使ってる…予測できん!」

正しい実装:機械的にランダム生成

code_verifierは必ず暗号学的に安全な方法で、毎回ランダムに生成する必要があります。

// ✅ 良い例:毎回異なる、予測不可能な愛言葉を生成
function generateCodeVerifier() {
  const array = new Uint8Array(32);  // 32バイトのランダムデータ
  crypto.getRandomValues(array);     // 暗号学的に安全な乱数
  return base64UrlEncode(array);     // URL安全な文字列に変換
}

const code_verifier = generateCodeVerifier();
// 例: "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"

なぜ機械的に生成するの?

理由説明
人間は弱い人が考える「ランダム」は実はパターンがある(誕生日、好きな言葉など)
十分な長さ推奨は43〜128文字。短いと総当たりで破られる
毎回違う同じ愛言葉の使い回しを防ぐ
予測不可能暗号学的に安全な乱数生成器(crypto.getRandomValues)を使う

X APIでの推奨仕様

OAuth2.0/PKCEの仕様(RFC 7636)では:

  • 長さ: 43〜128文字
  • 文字種: A-Z, a-z, 0-9, , ., _, ~ (Base64URL)
  • 生成方法: 暗号学的に安全な乱数生成器

code_challengeの計算方法

愛言葉(code_verifier)から、ヒント(code_challenge)を作ります。これはSHA-256ハッシュ関数を使って計算しますね。

ポイント

  • 一方向性: code_challengeからcode_verifierを逆算することは不可能(ハッシュ関数の一方向性)
  • 検証方法: 認可サーバーは「受け取ったcode_verifierをハッシュ化したら、最初のcode_challengeと一致するか」だけ確認できる

🔒 セキュリティまとめ

攻撃者の視点で考える

  1. 引換券(code)を盗聴した! → でも愛言葉(code_verifier)がわからないから食券に交換できない
  2. ヒント(code_challenge)は見えてる! → でもハッシュ化されてるから、元の愛言葉は逆算できない
  3. 前回の愛言葉を盗聴した! → 毎回ランダムに変わるから、次回は使えない

よくある実装ミス

code_verifier編

// ❌ ダメな例1:固定文字列
const code_verifier = "my_app_verifier";

// ❌ ダメな例2:短すぎる
const code_verifier = Math.random().toString(36).substring(7); // 7文字程度

// ❌ ダメな例3:予測可能
const code_verifier = `verifier_${Date.now()}`; // タイムスタンプは予測可能

// ❌ ダメな例4:弱い乱数
const code_verifier = Math.random().toString(36); // Math.random()は暗号学的に安全ではない

// ✅ 良い例:暗号学的に安全なランダム生成
const code_verifier = generateCodeVerifier(); // 前述の関数

redirect_uri編

// ❌ ダメな例:末尾のスラッシュが不一致
// Developer Portal登録: https://example.com/callback
const redirect_uri = "https://example.com/callback/"; // 末尾に / あり

// ✅ 良い例:完全一致
const redirect_uri = "https://example.com/callback";

state編

// ❌ ダメな例:stateの検証を忘れる
const code = new URLSearchParams(window.location.search).get('code');
// stateの確認をしていない!

// ✅ 良い例:stateを検証
const returnedState = new URLSearchParams(window.location.search).get('state');
const savedState = sessionStorage.getItem('oauth_state');
if (returnedState !== savedState) {
  throw new Error('State不一致!CSRF攻撃の可能性あり');
}

X API での具体的な設定

Developer Portalで設定すること

  1. Appの作成
  2. OAuth 2.0の有効化
    • App設定画面で「User authentication settings」を編集
    • OAuth 2.0を有効にする
  3. Callback URLの登録
    • 開発環境(Ngrok必要!): http://localhost:3000/callback
    • 本番環境: https://yourdomain.com/callback
    • 重要: 末尾のスラッシュまで含めて正確に登録
  4. Permissionsの設定
    • Read: ツイート読み取りのみ
    • Read and Write: ツイート読み取り+投稿
    • Read and Write and Direct Messages: DM含む全権限

まとめ

この記事で学んだこと

  1. OAuth2.0の必要性: パスワードを教えずに権限だけ渡せる仕組み
  2. 全体の流れ: ラーメン屋の例で理解する認可フロー
  3. PKCEの重要性: codeの盗聴・盗難を防ぐ仕組み
  4. code_verifierの生成: 機械的にランダム生成することの重要性
  5. 重要パラメータ: scope, redirect_uri, stateの役割

実装時の注意点

  • ✅ code_verifierは毎回ランダム生成(43文字以上)
  • ✅ 暗号学的に安全な乱数生成器を使用
  • ✅ redirect_uriは完全一致させる
  • ✅ stateでCSRF対策
  • ✅ 必要最小限のscopeを要求

次のステップ

この記事でOAuth2.0とPKCEの概念は理解できました!次は実際に実装してみましょう:

  1. X Developer Portalでアプリを作成
  2. 認証URL発行エンドポイントの実装
  3. コールバックエンドポイントの実装
  4. トークンを使ってAPI実行

実装編の記事もお楽しみに!


参考資料

推奨書籍

公式ドキュメント

ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

0人がこの投稿は役に立ったと言っています。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です