OAuth
パスワードを渡さずに権限を委譲する標準。「Googleでログイン」の裏側。
概要
OAuth(現行仕様は OAuth 2.0)は、「このアプリに、私の Google カレンダーの読み取りだけを許可する」のように、パスワードそのものを渡さずに限定的な権限を委譲するための標準プロトコルです。あるサービスが持っているあなたのデータへのアクセス権を、別のアプリに「一部だけ・期限付きで」貸し出す仕組み、と言い換えられます。
「Google でログイン」「GitHub と連携」といったボタンを押したとき、画面が一瞬 Google や GitHub に切り替わり、「このアプリに次の権限を許可しますか?」と確認されて元のアプリに戻ってくる — あの往復の裏側で動いているのが OAuth です。許可の証として発行される「アクセストークン」という語彙の出どころでもあります。
注意したいのは、OAuth はあくまで認可(何をしてよいかの許可)のプロトコルであって、認証(誰であるかの確認)の仕組みではないことです。認証まで含めた「ソーシャルログイン」は、OAuth の上に認証層を載せた OpenID Connect(OIDC)という拡張仕様によって実現されています。
なぜ生まれたか
OAuth 以前、あるサービスが別サービスのデータにアクセスするには、ユーザーが自分のパスワードを第三者アプリにそのまま入力するしかありませんでした。たとえば「Gmail の連絡先を読み込む」機能のために、Gmail のパスワードをそのアプリに預ける — アプリはメールの全削除もパスワード変更もできる全権を握り、ユーザーは後から「連絡先の読み取りだけ」に権限を絞ることも、そのアプリだけを無効化することもできません。パスワードを変えれば全連携が一斉に切れてしまいます。
この「全か無か」しかない委譲モデルを解決するために、2007年に OAuth 1.0 が、2012年により実装しやすく再設計された OAuth 2.0 が標準化されました。パスワードの代わりに「範囲(スコープ)と期限が限定されたトークン」を発行することで、権限を細かく貸し出し、いつでも個別に取り消せるようにしたのです。
詳細
登場人物は4つ
OAuth 2.0 には4つの役割が登場します。データの持ち主である「リソースオーナー」(=ユーザー)、データを使いたい「クライアント」(=連携アプリ)、許可を確認してトークンを発行する「認可サーバ」(例: Google のアカウント基盤)、そしてトークンと引き換えにデータを返す「リソースサーバ」(例: Google カレンダーの API)です。認可サーバとリソースサーバは同じ事業者が運営することが多いですが、役割としては別物です。
認可コードフローを順に追う
OAuth の中心にあるのが「認可コードフロー」です。リダイレクトが往復する少し込み入った流れですが、まず全体像を1枚の図で押さえてから、順を追って理解すると見通しが一気に良くなります。
まず(1)ユーザーがクライアントアプリで「Google と連携」ボタンを押すと、(2)クライアントはユーザーのブラウザを認可サーバの認可エンドポイントへリダイレクトさせます。この URL には「どのアプリが(client_id)」「どの権限を求めていて(scope)」「許可後にどこへ戻すか(redirect_uri)」が含まれます。(3)認可サーバはユーザー本人を認証し(ログイン済みなら省略)、「このアプリにカレンダーの読み取りを許可しますか?」という同意画面を表示します。(4)ユーザーが許可すると、認可サーバはブラウザを redirect_uri へリダイレクトで戻し、そのクエリパラメータに使い捨ての「認可コード」を添えます。ここまでが図の前半、ブラウザ経由の往復です。
ここからが後半です。(5)認可コードを受け取ったクライアントは、今度はブラウザを経由せず、サーバ間で直接、認可サーバのトークンエンドポイントに HTTP リクエストを送ります。認可コードに加えて、自分がその正規のアプリであることを示すクライアントシークレット(事前に発行された秘密の値)を添えます。(6)認可サーバは検証のうえアクセストークン(と多くの場合リフレッシュトークン)を返します。(7)以降クライアントは、リクエストの Authorization ヘッダにアクセストークンを添えてリソースサーバの API を呼び出し、許可された範囲のデータにアクセスできます。
この往復は、「次へ」で1ステップずつ進めながら、各時点で誰が何を持っているかを確かめると流れが定着します。
開始前。クライアントは事前登録により client_id とクライアントシークレット(秘密の値)を持っています。「次へ」でフローを進めてください。
なぜ二段階に分けるのか
「なぜ認可コードという中間物を挟むのか。最初からトークンを返せばよいのでは」というのが定番の疑問です。前半の往復はブラウザのリダイレクトを経由するため、URL は履歴やアクセスログに残り、傍受・漏えいのリスクがあります。そこで、ブラウザに露出する経路には「単体では使えない使い捨てのコード」だけを流し、本物のアクセストークンはクライアントシークレットで守られたサーバ間通信でのみ受け渡す — 経路の信頼度に応じて渡すものを分ける、というのがこの設計の核心です。もちろん全経路が HTTPS であることが大前提です。
なお、シークレットを安全に保持できない SPA やモバイルアプリでは、シークレットの代わりに PKCE(Proof Key for Code Exchange)という使い捨ての検証値でコード横取り攻撃を防ぐ方式が現在の標準です。かつて SPA 向けとされた「インプリシットフロー」(トークンを直接リダイレクトで返す方式)は、まさに上記のリスクのため非推奨になりました。
トークンの設計と実務の論点
アクセストークンは通常、数分〜1時間程度の短命に設定し、期限切れ後はリフレッシュトークンで再発行します。これにより、万一トークンが漏れても被害を時間的に限定できます。トークンの実体は認可サーバ側で管理する不透明な文字列のこともあれば、JWT として自己完結的に検証できる形式のこともあり、失効のしやすさと検証コストのトレードオフになります。
実務での典型的な落とし穴は、redirect_uri の検証を緩くして認可コードを攻撃者のサイトに送らせてしまうこと、scope を必要以上に広く要求してしまうこと、そして「OAuth でアクセストークンが取れた=本人確認ができた」と誤解して認証に流用してしまうことです。ログインが目的なら、ID トークンによって「誰がいつ認証されたか」を保証する OpenID Connect を使うのが正解です。