現代のWebサービスやモバイルアプリケーション開発において、OAuth 2.0とOpenID Connect (OIDC) は、もはや避けては通れない標準技術となりました。「Googleでログイン」や「Facebookでアカウント連携」といった機能は、これらのプロトコルのおかげで安全かつスムーズに実現されています。しかし、多くの開発者がこの二つの技術、特にそれらが発行する「トークン」の違いについて混乱を抱えています。OAuth 2.0が発行するアクセストークンと、OIDCが発行するIDトークン。これらは似て非なるものであり、その役割と目的は根本的に異なります。
私自身、フルスタック開発者としてキャリアをスタートさせた当初、この二つのプロトコルの関係性を理解するのに苦労した経験があります。「OAuth 2.0は認可のフレームワークで、OIDCはOAuth 2.0を拡張した認証のプロトコルである」という説明は何度も目にしましたが、それが具体的に何を意味するのか、なぜ二つが必要なのかが腹落ちしませんでした。この壁を乗り越える鍵こそが、「アクセストークン」と「IDトークン」の役割を正確に理解することだったのです。
この記事では、単にOAuth 2.0とOIDCの概要をなぞるのではなく、開発者が最も混乱しやすいIDトークンとアクセストークンの違いに焦点を当て、その核心を徹底的に解き明かします。それぞれのトークンが「誰のために」「何のために」存在するのか、その中身はどうなっているのか、そして実際のアプリケーションでどのように使い分けられるのか。この旅を終える頃には、あなたは自信を持って認証・認可フローを設計・実装できるようになっているはずです。
OAuth 2.0の基本:すべては「認可」のために
OpenID Connect (OIDC)を理解するためには、まずその土台であるOAuth 2.0をしっかりと理解する必要があります。OAuth 2.0は、しばしば「認証」プロトコルだと誤解されますが、その本質は「認可(Authorization)」、つまり「権限の委譲」にあります。ユーザーのパスワードのような強力なクレデンシャルを第三者のアプリケーション(クライアント)に渡すことなく、特定のリソースへの限定的なアクセス権を与えるためのフレームワークなのです。
OAuth 2.0が解決する課題:パスワード共有の悪夢
OAuth 2.0が登場する前、あるサービス(例:写真印刷サービス)が別のサービス(例:Googleフォト)に保存されているあなたの写真にアクセスしたい場合、どうしていたでしょうか?最も単純な方法は、写真印刷サービスにあなたのGoogleアカウントのIDとパスワードを教えることでした。しかし、これは極めて危険です。
- 過剰な権限: パスワードを渡してしまうと、写真印刷サービスはあなたの写真だけでなく、メール、カレンダー、連絡先など、Googleアカウントのすべてにアクセスできてしまいます。
- セキュリティリスク: 写真印刷サービスが悪意を持っていたり、セキュリティが脆弱だったりした場合、あなたのGoogleアカウント情報が漏洩・悪用される可能性があります。
- 権限の取り消しが困難: 一度パスワードを教えてしまうと、そのサービスからのアクセスだけを取り消すことは困難です。Googleのパスワードを変更するしかなく、それは他の連携サービスすべてに影響を与えます。
OAuth 2.0は、この「パスワード共有の悪夢」を解決するために生まれました。ユーザーはクライアントアプリケーションにパスワードを教える代わりに、リソースサーバー(Googleフォト)に対して「このアプリケーションに、私の写真データへの読み取りアクセス権を与えることを許可します」という同意を与えるのです。この同意に基づき、アクセストークンという「鍵」が発行され、クライアントはその鍵を使って限定されたリソースにのみアクセスできるようになります。
OAuth 2.0のアクセストークンは、ホテルのキーカードによく例えられます。
- フロント(認可サーバー)で本人確認(ユーザーの同意)を行うと、キーカード(アクセストークン)が渡されます。
- キーカードは、あなたの客室(保護されたリソース)のドアを開けることはできますが、他の客室やホテルの金庫を開けることはできません(限定された権限=スコープ)。
- キーカード自体は、あなたが誰であるか(名前、年齢、国籍など)を証明するものではありません。それは単に「特定の部屋へのアクセス許可証」に過ぎないのです。これが「認可」と「認証」の重要な違いです。
OAuth 2.0の登場人物(ロール)
OAuth 2.0のフローを理解するために、4つの主要な登場人物を把握しておく必要があります。
- リソースオーナー (Resource Owner): あなた、つまりリソース(例:写真データ)の所有者です。リソースへのアクセスを許可する権限を持っています。
- クライアント (Client): リソースオーナーの代わりに、保護されたリソースへアクセスしようとするアプリケーション(例:写真印刷サービス)。
- 認可サーバー (Authorization Server): リソースオーナーの同意を得て、クライアントに対してアクセストークンを発行するサーバー(例:Googleの認証・認可システム)。
- リソースサーバー (Resource Server): 保護されたリソースをホストしているサーバー(例:GoogleフォトのAPIサーバー)。クライアントからのリクエストを受け付け、提示されたアクセストークンを検証してリソースへのアクセスを許可または拒否します。
アクセストークン:リソースへの「合鍵」
OAuth 2.0フローの中心にあるのがアクセストークンです。これは、クライアントがリソースサーバーにリソースを要求する際に提示する、一時的な資格情報です。アクセストークンには以下の特徴があります。
- 目的: 特定のリソースへのアクセス権限を証明すること。
- 形式: RFC 6749では特定の形式を定めていません。多くの場合、クライアントにとっては意味不明なランダムな文字列(Opaque String)ですが、サーバー内部で検証可能なJWT(JSON Web Token)形式であることもあります。重要なのは、クライアントはアクセストークンの中身を解釈すべきではないということです。クライアントの役割は、受け取ったトークンをリソースサーバーへのリクエストに含めて送ることだけです。
- 有効期間: 通常は比較的短い有効期間(数分〜数時間)が設定されます。漏洩時のリスクを低減するためです。
- スコープ: アクセストークンに紐付けられた権限の範囲です。「`photos.read`(写真の読み取り)」や「`calendar.write`(カレンダーへの書き込み)」のように、非常に限定的な権限が付与されます。
クライアントは、このアクセストークンをHTTPリクエストの`Authorization`ヘッダーに含めてリソースサーバーに送信します。
GET /photos/v1/all HTTP/1.1
Host: photos.googleapis.com
Authorization: Bearer AbCdEf123456...
リソースサーバーは受け取ったアクセストークンを検証し、トークンが有効で、要求された操作に必要なスコープを持っていることを確認した上で、リソースを返却します。
多様なユースケースに応える「グラントタイプ」
OAuth 2.0の強力な点の一つは、「グラントタイプ(Grant Type)」と呼ばれる複数のアクセストークン取得フローを定義していることです。これにより、Webサーバーアプリケーション、JavaScriptで動作するシングルページアプリケーション(SPA)、ネイティブモバイルアプリ、サーバー間通信など、さまざまなクライアントの種類やユースケースに対応できます。
| グラントタイプ | 概要 | 主なユースケース | セキュリティ |
|---|---|---|---|
| 認可コード (Authorization Code) | 最も一般的で安全なフロー。ユーザーを認可サーバーにリダイレクトし、同意後に「認可コード」をクライアントに払い出す。クライアントはそのコードを使ってサーバーサイドでアクセストークンと交換する。 | サーバーサイドのWebアプリケーション(例: Ruby on Rails, Django) | 非常に高い。認可コードとアクセストークンがブラウザに直接公開されないため。 |
| 認可コード + PKCE | 認可コードフローにPKCE (Proof Key for Code Exchange) という仕組みを追加したもの。認可コードの横取り攻撃を防ぐ。 | ネイティブモバイルアプリ、シングルページアプリケーション (SPA) | 極めて高い。現在のパブリッククライアント(サーバーサイドの秘密情報を保持できないクライアント)におけるベストプラクティス。 |
| インプリシット (Implicit) | (レガシー)認可サーバーからアクセストークンが直接ブラウザに返されるシンプルなフロー。 | SPA。しかし現在では非推奨。セキュリティ上の脆弱性があるため、PKCE付き認可コードフローの使用が強く推奨される。 | 低い。アクセストークンがURLフラグメントに含まれ、漏洩リスクが高い。リフレッシュトークンも使用できない。 |
| リソースオーナーパスワードクレデンシャル | (レガシー)クライアントがユーザーのIDとパスワードを直接収集し、それを使ってアクセストークンを取得する。 | 自社サービス内で、ユーザーに高い信頼がある場合(例: 公式モバイルアプリ)。しかし、OAuth 2.0の原則に反するため、極力避けるべき。 | 低い。クライアントがユーザーのパスワードを直接扱う必要があり、リスクが大きい。 |
| クライアントクレデンシャル (Client Credentials) | ユーザーが介在しない、サーバー間(マシンツーマシン)通信で使用される。クライアント自身の権限でリソースにアクセスする。 | マイクロサービス間のAPI呼び出し、バッチ処理など。 | 高い。クライアントIDとクライアントシークレットで認証するため、安全に管理されれば堅牢。 |
このように、OAuth 2.0はあくまで「認可」のための柔軟で強力なフレームワークを提供します。しかし、お気づきでしょうか? ここまでの話には、「ユーザーが誰であるか」をクライアントが知るための標準的な方法がありません。クライアントはリソースへのアクセス権を得ましたが、ログインしているユーザーのIDや名前、メールアドレスといったアイデンティティ情報をどうやって確実に手に入れれば良いのでしょうか? このギャップを埋めるのが、次に登場するOpenID Connectです。
OpenID Connect (OIDC)の登場:「認証」レイヤーの追加
OAuth 2.0が「誰が何をして良いか(認可)」を定義するフレームワークであるのに対し、OpenID Connect (OIDC) は「その人が誰であるか(認証)」を証明するためのプロトコルです。重要なのは、OIDCがOAuth 2.0を置き換えるものではなく、OAuth 2.0の上に構築された薄いアイデンティティレイヤーであるという点です。つまり、OIDCはOAuth 2.0の認可フローをそのまま利用し、そこに「認証」という概念を標準化された形で追加します。
なぜOIDCが必要だったのか?
OAuth 2.0だけでは、クライアントアプリケーションがユーザーを「認証」するのに不十分でした。前述の通り、アクセストークンはリソースサーバーのためのものであり、クライアントがその中身を解釈することは想定されていません。では、OIDCが登場する前はどうしていたのでしょうか?
多くのサービスでは、クライアントがアクセストークンを取得した後、そのトークンを使って「ユーザー情報API(例: `/me` や `/userinfo` エンドポイント)」を呼び出し、ユーザーのIDや名前を取得していました。GoogleやFacebookなどの大手プロバイダーは、それぞれ独自のAPIエンドポイントとデータ形式を提供していました。
- 標準化の欠如: プロバイダーごとにユーザー情報APIの仕様が異なり、クライアントはプロバイダーごとに実装を変える必要がありました。
- 追加のAPIコール: ユーザー情報を取得するために、必ず追加のネットワークリクエストが発生し、認証プロセスに遅延が生じていました。
- 認証イベントの不明確さ: ユーザー情報APIから情報が取得できても、それが「いつ」「どのように」認証された結果なのかという情報が欠けていました。
OIDCは、これらの問題を解決するために生まれました。OAuth 2.0のフローの中で、認証に関する情報を標準化された形式でクライアントに直接渡す仕組み、それがIDトークンです。
IDトークン:ユーザー認証の「身分証明書」
OIDCの心臓部と言えるのがIDトークンです。これは、認可サーバーによって電子署名されたJWT (JSON Web Token) 形式のトークンで、認証されたユーザーに関する情報(クレーム)を含んでいます。クライアントは、このIDトークンを受け取り、その署名を検証することで、ユーザーが確かに認可サーバーによって認証された本人であることを確認できます。
IDトークンは、前述の「ユーザー情報API」への追加の呼び出しを不要にします。アクセストークンと共にIDトークンが発行されることで、クライアントはトークン交換のレスポンスを受け取った瞬間に、ユーザーが誰であるかを知ることができるのです。
JWT (JSON Web Token) の仕組み
IDトークンを理解するためには、その形式であるJWTについて知る必要があります。JWTは、`ヘッダー.ペイロード.署名` という3つの部分をドット(`.`)で連結した文字列です。それぞれの部分はBase64Urlエンコードされています。
例: `eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiIxMjM0NS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImF1ZCI6IjEyMzQ1LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwic3ViIjoiMTA5ODc2NTQzMjEwIiwibmFtZSI6IkphbmUgRG9lIiwiZXhwIjoxNjY4NTk1MjAwLCJpYXQiOjE2Njg1OTE2MDB9.SignatureValueHere`
- ヘッダー (Header): トークンの種類(`typ`)と、署名に使われるアルゴリズム(`alg`、例: `RS256`, `HS256`)についての情報が含まれます。
{ "alg": "RS256", "typ": "JWT", "kid": "a1b2c3d4..." } - ペイロード (Payload): クレーム(Claims)と呼ばれる、ユーザー情報やトークンのメタデータが含まれる部分です。IDトークンにおける主要なクレームには以下のようなものがあります。
{ "iss": "https://accounts.google.com", // (Issuer) トークンの発行者 "sub": "109876543210123456789", // (Subject) ユーザーの一意な識別子 "aud": "12345-abc.apps.googleusercontent.com", // (Audience) トークンの対象者(クライアントID) "exp": 1668595200, // (Expiration Time) トークンの有効期限 (Unixタイムスタンプ) "iat": 1668591600, // (Issued At) トークンの発行日時 (Unixタイムスタンプ) "name": "Jane Doe", // ユーザーの名前 "email": "jane.doe@example.com", // ユーザーのメールアドレス "nonce": "n-0S6_WzA2Mj" // リプレイ攻撃を防ぐための値 } - 署名 (Signature): ヘッダーとペイロードを、ヘッダーで指定されたアルゴリズムと秘密鍵を使って署名したものです。この署名により、トークンが改ざんされていないこと、そして確かに信頼できる発行者(認可サーバー)によって発行されたものであることを検証できます。
クライアントは、認可サーバーが公開している公開鍵を使ってこの署名を検証します。もし署名が正しければ、ペイロードに含まれる情報は信頼できるものと判断し、ユーザーの認証を完了させることができます。この自己完結的な検証可能性が、IDトークンの強力な特徴です。
IDトークン vs. アクセストークン 徹底比較
ここまでで、OAuth 2.0のアクセストークンとOIDCのIDトークンのそれぞれの役割が見えてきました。ここで一度、両者の違いを明確にするために、詳細な比較表を見てみましょう。この表を理解することが、二つのプロトコルの核心を掴む上で最も重要です。
| 比較項目 | IDトークン (ID Token) | アクセストークン (Access Token) |
|---|---|---|
| プロトコル | OpenID Connect (OIDC) | OAuth 2.0 |
| 主な目的 | 認証 (Authentication) ユーザーが誰であるかを証明する。 |
認可 (Authorization) クライアントが何をして良いかを証明する。 |
| 主な受信者(利用者) | クライアントアプリケーション クライアントがユーザーを認証し、セッションを作成するために利用する。 |
リソースサーバー (API) APIがリクエスト元のクライアントのアクセス権限を検証するために利用する。 |
| 形式 | JWT (JSON Web Token) 形式であることが必須。 | 規定なし。JWT形式の場合もあれば、単なるランダムな文字列(Opaque)の場合もある。クライアントは形式に依存すべきではない。 |
| 内容 | ユーザーの識別子 (sub)、発行者 (iss)、クライアントID (aud)、有効期限 (exp) などの認証イベントに関する情報。名前やメールアドレスなどのユーザープロファイル情報も含むことができる。 |
リソースサーバーがアクセス権を判断するための情報。ユーザーID、クライアントID、スコープなど。クライアントは内容を解読しようとしてはならない。 |
| 検証方法 | クライアントは、認可サーバーが公開する公開鍵を用いてJWTの署名を検証する。これにより、トークンの真正性と完全性を確認する。 | リソースサーバーが検証する。検証方法はリソースサーバーの実装に依存する(例:認可サーバーへの問い合わせ、JWT署名の検証など)。 |
| 自己完結性 | 高い。IDトークン自体が署名付きの認証情報を含んでいるため、クライアントは外部に問い合わせることなくユーザーを検証できる。 | 低い(場合がある)。Opaqueなトークンの場合、リソースサーバーは認可サーバーに問い合わせる(Introspection)必要がある。 |
| 通信経路での使われ方 | 認可サーバーからクライアントへ一度渡される。その後、APIリクエストなどに使用されることはない。 | クライアントからリソースサーバーへの各APIリクエストに添付(通常は`Authorization`ヘッダー)されて繰り返し使用される。 |
なぜこの違いが重要なのか?
この比較表から、二つのトークンが全く異なる目的とライフサイクルを持つことがわかります。
IDトークンはクライアントのための「身分証明書」です。クライアントは受け取った証明書を見て、「ああ、この人は確かにGoogleが認証したJane Doeさんだな」と確認し、自社サービスのセッションを開始します。一度確認したら、その証明書を毎回見せびらかす必要はありません。
一方、アクセストークンはリソースサーバー(API)のための「通行許可証」です。クライアントは保護されたAPIにアクセスするたびに、この許可証を提示しなければなりません。APIの受付係は、許可証が有効か、そして目的の操作(例:「写真を見る」)が許可されているかを確認します。
この役割分担を誤ると、重大なセキュリティ問題を引き起こす可能性があります。例えば、アクセストークンを認証の目的で使ってしまうとどうなるでしょうか? あるアクセストークンがJWT形式で、中にユーザーIDが含まれていたとしても、その`aud`(Audience)クレームがリソースサーバー向けになっているかもしれません。クライアントがそれを自分のための認証情報だと誤解して受け入れてしまうと、意図しないアクセスを許可してしまう「Confused Deputy Problem」のような脆弱性につながる恐れがあります。
OIDCの仕様は、クライアントがIDトークンを厳密に検証すること(`iss`, `aud`, `exp`, `nonce`などのチェック)を要求しています。 これにより、クライアントは安全にユーザーを認証できるのです。
実践シナリオ:ソーシャルログインで見るOAuth 2.0とOIDC
理論だけではイメージが湧きにくいかもしれません。ここでは、最も一般的なOIDCのユースケースである「ソーシャルログイン」を例に、IDトークンとアクセストークンがどのように連携して機能するのかをステップバイステップで見ていきましょう。ここでは、PKCE付き認可コードフローを想定します。
-
ステップ1: ユーザーがログインボタンをクリック
ユーザーがSuperStoreの「Googleでログイン」ボタンをクリックします。SuperStore(クライアント)は、ユーザーをGoogle(認可サーバー)の認可エンドポイントにリダイレクトさせます。このとき、リクエストにはいくつかの重要なパラメータが含まれます。
response_type=code: 認可コードフローを使用することを示します。client_id=...: SuperStoreのクライアントIDです。redirect_uri=...: 認可後にGoogleがユーザーを戻す先のURLです。scope=openid email profile https://www.googleapis.com/auth/calendar.events: ここが重要です。openidスコープを含めることで、OIDCフローを要求していることを示します。emailとprofileはIDトークンに含めてほしい情報(クレーム)を指定します。calendar.eventsはOAuth 2.0のスコープで、カレンダーへのアクセス許可を求めています。state=...: CSRF攻撃を防ぐためのランダムな文字列です。nonce=...: IDトークンのリプレイ攻撃を防ぐためのランダムな文字列です。code_challenge=...: PKCEのためのパラメータです。
-
ステップ2: ユーザーの認証と同意
ユーザーはGoogleのログイン画面に遷移します。すでにログイン済みの場合は、同意画面が表示されます。同意画面には、「SuperStoreがあなたの名前、メールアドレス、プロフィール情報を閲覧し、カレンダーにイベントを追加することを許可しますか?」といった内容が表示されます。ユーザーは内容を確認し、「許可する」をクリックします。
-
ステップ3: 認可コードの受け取り
GoogleはユーザーをSuperStoreの
redirect_uriにリダイレクトさせます。このリダイレクトURLのクエリパラメータには、一時的な認可コードと、ステップ1で送ったstate値が含まれています。SuperStoreのサーバーサイドは、まずstate値が自身が生成したものと一致するかを検証し、CSRF攻撃でないことを確認します。 -
ステップ4: トークン交換
SuperStoreのサーバーは、受け取った認可コードを使い、Googleのトークンエンドポイントにリクエストを送信します。このリクエストはサーバー間通信であり、ユーザーのブラウザは介しません。リクエストには認可コード、クライアントID、クライアントシークレット、そしてPKCEのための
code_verifierが含まれます。 -
ステップ5: IDトークンとアクセストークンの取得
Googleのトークンエンドポイントは、リクエストを検証し、問題がなければJSONレスポンスを返します。このレスポンスに、IDトークンとアクセストークンの両方が含まれています。
{ "access_token": "ya29.A0... (リソースアクセス用のトークン)", "expires_in": 3599, "refresh_token": "1//04... (アクセストークン再取得用のトークン)", "scope": "https://www.googleapis.com/auth/calendar.events openid https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email", "token_type": "Bearer", "id_token": "eyJhbGciOiJSUzI1Ni... (ユーザー認証用のJWT)" } -
ステップ6: IDトークンによる認証
SuperStoreのサーバーは、まずIDトークンを処理します。
- Googleの公開鍵を使って署名を検証します。
issが`https://accounts.google.com`であることを確認します。audがSuperStore自身のクライアントIDであることを確認します。expが現在時刻より未来であることを確認します。nonceがステップ1で生成したものと一致することを確認します。
これらの検証がすべて成功したら、SuperStoreは「このユーザーは確かにGoogleによって認証された本人である」と結論付けます。IDトークンのペイロードからユーザーのID(`sub`)、名前、メールアドレスを取得し、自社のデータベースと照合(または新規作成)して、ユーザーのログインセッションを開始します。
-
ステップ7: アクセストークンによるAPIアクセス
後日、ユーザーが商品を購入し、SuperStoreが配送予定日をユーザーのGoogleカレンダーに登録する必要が生じたとします。このとき、SuperStoreはステップ5で受け取ったアクセストークンを使用します。Google Calendar APIのエンドポイントに対し、このアクセストークンを`Authorization: Bearer`ヘッダーに含めてリクエストを送信します。
Google Calendar API(リソースサーバー)は、アクセストークンを受け取り、それが有効で、
https://www.googleapis.com/auth/calendar.eventsスコープを持っていることを確認し、カレンダーへのイベント登録を許可します。
この一連の流れから、認証はIDトークンで完結し、認可(APIアクセス)はアクセストークンが担うという明確な役割分担が、実際のアプリケーションでどのように機能するかがお分かりいただけたかと思います。
セキュリティ観点から見たベストプラクティス
OAuth 2.0とOIDCは強力なフレームワークですが、その力を最大限に引き出し、安全なアプリケーションを構築するためには、いくつかの重要なセキュリティプラクティスを遵守する必要があります。特にトークンの扱いには細心の注意が必要です。
IDトークンの検証は必須
前述の通り、クライアントは受け取ったIDトークンを必ず検証しなければなりません。これはOIDCの仕様で定められた義務です。最低でも以下の項目をチェックしてください。
- 署名の検証: トークンが改ざんされておらず、信頼できる発行者からのものであることを確認する最も重要なステップです。認可サーバーのJWKS (JSON Web Key Set) エンドポイントから公開鍵を取得して検証します。
- 発行者 (`iss`) の検証: 自分が意図した認可サーバーから発行されたトークンであることを確認します。
- 対象者 (`aud`) の検証: トークンが自分(自社のクライアントID)宛てに発行されたものであることを確認します。これにより、他のクライアントに発行されたトークンを誤って受け入れてしまう攻撃を防ぎます。
- 有効期限 (`exp`) の検証: トークンが有効期限切れでないことを確認します。
- Nonce の検証: 認証リクエスト時に指定した`nonce`値と、IDトークン内の`nonce`クレームが一致することを確認します。これにより、攻撃者が過去に盗聴したIDトークンを再利用するリプレイ攻撃を防ぎます。
アクセストークンの適切な管理
アクセストークンはリソースへのアクセス権を持つ強力なキーです。その管理は慎重に行う必要があります。
- クライアントはアクセストークンの中身に依存しない: たとえアクセストークンがJWT形式であっても、クライアントはそれをパースして認証などに利用してはいけません。アクセストークンはリソースサーバーのためのものです。
- 適切なスコープを要求する: 最小権限の原則に従い、アプリケーションが必要とする最小限のスコープのみを要求しましょう。不要な権限を要求することは、ユーザーに不信感を与え、万が一トークンが漏洩した際のリスクを増大させます。
- 短い有効期間とリフレッシュトークンの活用: アクセストークンの有効期間は可能な限り短く(例: 15分〜1時間)設定します。これにより、漏洩時の影響範囲を限定できます。長期間のアクセスが必要な場合は、アクセストークンよりも有効期間が長いリフレッシュトークンを併用します。リフレッシュトークンはアクセストークンの再取得専用のトークンであり、厳重に保管する必要があります。
- 安全なトークン保管:
- Webサーバーアプリケーション: サーバーサイドのセッションストアなど、クライアント(ブラウザ)からアクセスできない場所にトークンを保管します。
- シングルページアプリケーション (SPA): トークンをブラウザのLocalStorageやSessionStorageに保存するのは、XSS(クロスサイトスクリプティング)攻撃に対して脆弱であるため推奨されません。現在のベストプラクティスは、BFF (Backend for Frontend) パターンを採用し、トークンをサーバーサイドで管理し、ブラウザには`HttpOnly`, `Secure`, `SameSite=Strict`属性を付けたセッションクッキーを渡す方法です。
- ネイティブモバイルアプリ: OSが提供するセキュアなストレージ(iOSのKeychain, AndroidのKeystore)にトークンを保管します。
フロー全体のセキュリティ
- 最新のグラントタイプを使用する: パブリッククライアント(SPAやモバイルアプリ)では、PKCE付き認可コードフローが標準です。インプリシットフローはレガシーであり、使用は避けるべきです。
- `state` パラメータの利用: 認可リクエスト時に`state`パラメータを使い、リダイレクト時にその値が一致することを検証することで、CSRF(クロスサイトリクエストフォージェリ)攻撃を防ぎます。
- リダイレクトURIの厳格な検証: 認可サーバー側で、登録済みのリダイレクトURIと完全に一致する場合のみリダイレクトを許可するように設定します。これにより、認可コードやトークンが攻撃者のサイトに送られてしまうことを防ぎます。
まとめ:認可と認証、それぞれの役割を正しく使い分ける
本記事を通して、OAuth 2.0とOpenID Connect、そしてそれぞれが発行するアクセストークンとIDトークンの明確な違いについて、深く掘り下げてきました。最後に、最も重要なポイントを再確認しましょう。
- OAuth 2.0は「認可」のフレームワークです。 その主役はアクセストークンであり、これはクライアントがリソースサーバーに対して「何をする権限があるか」を証明するための「通行許可証」です。
- OpenID Connectは、OAuth 2.0の上に構築された「認証」のプロトコルです。 その主役はIDトークンであり、これはクライアントがユーザーの身元を確認するための、電子署名された「身分証明書」です。
- IDトークンはクライアントのためのもの、アクセストークンはリソースサーバー(API)のためのものです。 この役割分担を絶対に混同してはなりません。
- 「ユーザーが誰かを知りたい、ログイン処理を行いたい」 → IDトークンが必要です。これはOIDCの領域です。
- 「ユーザーの代わりに、外部のAPI(Google Calendar, Twitter API 등)を呼び出したい」 → アクセストークンが必要です。これはOAuth 2.0の領域です。
現代の多くの「ソーシャルログイン」機能は、この両方を必要とするため、必然的にOAuth 2.0のフローを利用したOIDCプロトコルを使用することになります。
フルスタック開発者として、私たちは日々の業務でAPIを設計し、フロントエンドで認証状態を管理し、ユーザーデータを保護するという責務を負っています。この文脈において、アクセストークンとIDトークンの違いを正確に理解し、それぞれを仕様に準拠した形で正しく使い分けることは、単なる技術的な知識にとどまらず、堅牢で安全なアプリケーションを構築するための基礎となるスキルです。この知識が、あなたの次のプロジェクトで、よりセキュアで信頼性の高い認証・認可基盤を構築する一助となることを心から願っています。
今後、認証・認可のフローを実装する際には、ぜひ一度立ち止まって考えてみてください。「今、私が扱っているこのトークンは、誰のためのもので、何を証明するためのものだろうか?」と。その問いに明確に答えられるようになったとき、あなたはOAuth 2.0とOIDCの核心を真に理解したと言えるでしょう。
Post a Comment