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で管理する」という設計が一般的です。