OAuthのトークンの種類とBearer認証を整理した話

GitHub OAuthのログインを実装する中で、複数の種類のトークンが登場しました。 それぞれの役割・寿命・保存場所が異なるため、整理してまとめます。

Bearer認証とは

「Bearer(ベアラー)」は英語で「持参人」を意味します。「このトークンを持っている人を信頼して処理を許可する」 という考え方の認証方式です。

HTTPヘッダーでは以下のように使います。

Authorization: Bearer ghp_xxxxxxxxxxxx

GitHubのAPIを叩くコードで言うとこの部分です。

const githubRes = await fetch('https://api.github.com/user', {
  headers: {
    Authorization: `Bearer ${accessToken}`,
  }
});

GitHubのAPIサーバーはこのヘッダーを見て「このアクセストークンを持っているなら正規のリクエストだ」と判断してユーザー情報を返します。

Bearerトークンの特徴

トークンを持っているだけで認証される

パスワード認証と違い、誰が送ってきたかを問いません。トークンさえ正しければ認証が通ります。

パスワード認証: ID + パスワードの組み合わせで認証
Bearer認証:    トークン1つで認証(IDなし)

トークンが漏れると危険

トークンを持っている人を無条件に信頼するため、第三者に漏れると不正利用されます。そのため httpOnly Cookie に保存してJavaScriptからアクセスできないようにしたり、HTTPS通信で盗聴を防ぐといった対策が必要です。

Authorizationヘッダーのスキーム

Bearer 以外にも認証スキームがあります。

スキーム 形式 用途
Bearer Bearer トークン OAuth・JWT など
Basic Basic base64(ID:パスワード) シンプルなID/パスワード認証
API-Key API-Key キー APIキー認証(非標準だが慣例)

WebAPIでは Bearer が最も広く使われています。


OAuthで登場する3種類のトークン

GitHub OAuthの実装では3種類のトークンが登場します。

① Authorization Code(認可コード)

GitHubがコールバックURLのクエリパラメータに付けて返してくる引換券です。

/login/github/callback?code=ABC123...&state=xxx
項目 内容
発行者 GitHub
寿命 約10分(非常に短い)
使い回し 1回限り(使うと無効になる)
保存場所 URLに含まれるだけ(保存しない)
用途 アクセストークンと交換するための引換券

なぜURLに直接アクセストークンを返さないのか?

URLはブラウザの履歴・サーバーのログ・リファラーヘッダーなどに残ります。アクセストークンをURLに含めると漏洩リスクが高まるため、短命な引換券(code)をURLに載せてアクセストークンの取得はサーバー間通信で行います。

ブラウザ(URLに残る)  → code(短命・使い捨て)
サーバー間通信(安全)  → アクセストークン(本物)

② Access Token(アクセストークン)

codeをアクセストークンに交換する処理の裏側はこうなっています。

自分のサーバー → GitHubのAPIにPOSTリクエスト
  https://github.com/login/oauth/access_token
    code=ABC123...
    client_id=xxxx
    client_secret=xxxx    ← 秘密鍵(サーバーだけが知っている)

← アクセストークン(ghp_xxx)が返ってくる
項目 内容
発行者 GitHub
寿命 GitHubの設定次第(デフォルトは無期限)
保存場所 サーバーのメモリ(一時的)。DBには保存しない
用途 GitHubのAPIを叩く許可証

今回の実装ではユーザー情報を取得したら使い捨てにしています。

const tokens = await github.validateAuthorizationCode(code);
const accessToken = tokens.accessToken();

// アクセストークンでAPIを叩く
const githubRes = await fetch('https://api.github.com/user', {
  headers: { Authorization: `Bearer ${accessToken}` }
});

// 取得できたらアクセストークンは不要(DBに保存しない)
const githubUser = await githubRes.json();

③ Refresh Token(リフレッシュトークン)

アクセストークンが期限切れになったとき、ユーザーに再ログインさせずに新しいアクセストークンを取得するためのトークンです。

アクセストークンが期限切れ
  → リフレッシュトークンを使って新しいアクセストークンを取得
  → ユーザーはログインし直さなくてよい
項目 内容
発行者 GitHub
寿命 アクセストークンより長い(数ヶ月〜1年)
保存場所 DBに安全に保存する必要がある
用途 アクセストークンの再取得

GitHubのOAuthアプリはデフォルトでアクセストークンが無期限のため、リフレッシュトークンは発行されません。Google・Twitterなどの他プロバイダではアクセストークンに有効期限があるため必要になります。


全体の流れと各トークンの位置づけ

① ブラウザ ←→ GitHubの認証画面
     ↓ code(引換券・短命・使い捨て)を受け取る

② 自分のサーバー ←→ GitHubのAPIサーバー
     ↓ codeをアクセストークンに交換(サーバー間通信)

③ 自分のサーバー → GitHubのAPIを叩く
     ↓ アクセストークンでユーザー情報を取得して使い捨て

④ 自分のサーバー → D1にセッションIDを保存
     ↓ CookieにセッションIDをセット(自前のセッション管理)
トークン 発行者 寿命 今回の扱い
Authorization Code GitHub 約10分・1回限り URLから取り出してすぐ交換
Access Token GitHub 無期限(GitHubデフォルト) APIを叩いたら使い捨て
Refresh Token GitHub 数ヶ月〜 今回は発行されない
セッションID 自分 30日 D1とCookieで管理

GitHubのアクセストークンをそのまま使わない理由

シンプルに「GitHubのアクセストークンをそのままCookieに保存する」方法もあります。しかし以下の問題があります。

問題 内容
漏洩リスク GitHubのトークンが漏れるとGitHubのAPI全体を不正利用される
無効化できない 自前のセッションならD1から削除して即座にログアウトできるが、GitHubのトークンは自分では無効化できない
情報の保持 GitHubのトークンにはアプリ独自の情報(権限など)を持たせられない

そのため「GitHubのトークンはユーザー情報の取得にだけ使い、自前のセッションIDで管理する」という設計が一般的です。

← トップページに戻る