認証は、利用者が誰かを確かめることです。
認可は、その利用者やシステムに何を許すかを決めることです。
OAuth 2.0は主にAPIへのアクセス権を委譲するための枠組みであり、OpenID Connect(OIDC)はOAuth 2.0の上に認証の仕組みを加えます。
Amazon CognitoやAuth0は両方に対応するため、サービス名ではなく「誰を認証し、どのAPIへのアクセスを許すのか」から設計を始める必要があります。
OAuth 2.0とOIDCの違い
OAuth 2.0が扱う中心はアクセストークンです。
アクセストークンは、対象のAPIに対して許可された操作を示しますが、アプリのログイン状態を証明するためのトークンではありません。
OpenID Connect(OIDC)では、認証要求にopenidスコープを含め、認証結果を表すIDトークンを受け取ります。
IDトークンはクライアントアプリが利用し、アクセストークンは対象のAPIが利用するという分担が基本です。
| 比較項目 | OAuth 2.0 | OIDC |
|---|---|---|
| 主な目的 | APIやリソースへのアクセスを認可する | 利用者を認証し、認証結果をクライアントへ伝える |
| 中心となるトークン | アクセストークン | IDトークンとアクセストークン |
| 主な受け手 | リソースサーバー(API) | IDトークンはクライアント、アクセストークンはAPI |
| 代表的な用途 | API呼び出し、機械間通信 | ログイン、シングルサインオン、利用者情報の取得 |
| スコープ | APIごとに定義した権限 | openidを起点に、必要に応じてprofileやemailを追加 |
アクセストークンをログイン判定に使わない理由
アクセストークンの宛先はAPIであり、クライアントアプリではありません。
形式も常にJWTとは限らないため、クライアントが中身を読み、利用者のログイン証明として扱う設計は避けます。
ログイン後のセッションを開始する際はIDトークンを使い、署名を確認するだけでなく、発行者を示すiss、対象クライアントを示すaud、有効期限のexp、要求時に送ったnonceなどを、利用するライブラリの手順に従って検証します。
IDトークンは署名されたJWTの一種であり、デコードと検証は別の処理です。
一方、APIはアクセストークンの発行者、宛先、有効期限、署名、必要なスコープを検証してから処理を許可します。
Authorization Code FlowとPKCEの流れ
利用者がログインするWebアプリ、SPA、モバイルアプリでは、Authorization Code FlowにPKCEを組み合わせる構成が基準になります。
PKCEは、認可要求を始めたクライアントだけが認可コードをトークンへ交換できるようにする仕組みです。
- クライアントは推測困難な
code_verifierを作り、変換したcode_challengeを認可要求に含めます。 - 認可サーバーは利用者を認証し、登録済みのリダイレクトURIへ認可コードを返します。
- クライアントは認可コードと
code_verifierをトークンエンドポイントへ送ります。 - 認可サーバーは対応関係を確認し、IDトークンとアクセストークンを返します。
- クライアントはIDトークンを検証し、APIは受け取ったアクセストークンを検証します。
OAuth 2.0の最新のセキュリティ指針であるRFC 9700は、アクセストークンがブラウザー経由の認可レスポンスに現れるImplicit Grantを新しい設計で選ばず、Authorization Code Flowを使うよう勧めています。
用途ごとのフロー選択
| 利用場面 | 基本となる選択 | 設計上の要点 |
|---|---|---|
| SPAやモバイルアプリのログイン | OIDCのAuthorization Code FlowとPKCE | クライアントシークレットに依存せず、認可コードの横取りに備える |
| サーバー側で処理するWebアプリのログイン | OIDCのAuthorization Code FlowとPKCE | クライアント認証に加えてPKCEを使い、トークンをサーバー側で扱う |
| 機械間通信 | OAuth 2.0のClient Credentials Grant | 人間の利用者を認証しないためIDトークンは使わず、API用のスコープを絞る |
| 外部IdPを使うログイン | OIDCによるフェデレーション | IdPとRelying Partyの役割、発行者、クライアントID、属性対応を先に決める |
リフレッシュトークンが必要な場合は、offline_accessなどサービスが定める条件を確認し、発行設定、保存場所、ローテーション、失効方法まで設計します。
Amazon Cognitoで確認すること
Amazon Cognitoのユーザープールは、アプリから見ればOIDCのIdentity Provider(IdP)として動作します。
一方、Auth0などの外部IdPをユーザープールへ登録する構成では、CognitoがRelying Partyとなり、外部IdPから受け取った情報をユーザープールの属性へ対応付けます。
この方向を曖昧にすると、発行者URL、クライアントID、コールバックURL、スコープ、属性マッピングをどちらへ設定するのか判断できません。
Cognitoが発行するIDトークンとアクセストークンはJWTですが、リフレッシュトークンはアプリが内容を解釈しない不透明な値として扱います。
外部OIDC IdPとの設定項目は、AWSのユーザープールにOIDC IdPを追加する公式手順で確認できます。
Auth0で確認すること
Auth0のIDトークンはJWTですが、アクセストークンの形式は宛先や構成によって異なるため、クライアントは不透明な値として扱います。
独自APIへ渡すアクセストークンでは、APIの識別子をaudienceとして要求し、API側で宛先とスコープを検証します。
offline_accessを指定するだけでリフレッシュトークンが必ず発行されるわけではなく、対象APIの設定や許可されたフローも一致している必要があります。
トークンごとの用途は、Auth0の公式トークン解説と、利用中のSDKの手順を基準にします。
CognitoとAuth0を連携する手順
両サービスを接続する前に、どちらが利用者を認証するIdPで、どちらが認証結果を受け取るRelying Partyなのかを一文で定義します。
たとえばAuth0を外部IdP、CognitoユーザープールをRelying Partyとする場合は、次の順に設定を照合します。
- Auth0側でCognito用のアプリケーションを登録し、Cognitoが指定するコールバックURLを許可します。
- Cognito側へAuth0の発行者URL、クライアントID、クライアントシークレット、要求するスコープを登録します。
subやemailなど、実際に必要なクレームだけをCognitoのユーザー属性へ対応付けます。- Cognitoのアプリクライアントで外部IdPを有効にし、アプリが受け取るCognito発行トークンの検証条件を固定します。
- ログイン、ログアウト、期限切れ、同意拒否、属性不足、鍵更新をテストします。
外部IdPが発行したトークンとCognitoがアプリへ発行するトークンは別物なので、発行者や宛先を取り違えないようにします。
実装前のチェックリスト
- 認証と認可の要件を分けて記述しているか。
- ログイン要求に
openidを含め、必要な属性に応じてスコープを絞っているか。 - Authorization Code FlowとPKCEを使い、Implicit Grantを新規採用していないか。
- リダイレクトURIを事前登録し、環境ごとの値を混同していないか。
- IDトークンとアクセストークンの受け手、用途、検証条件を分けているか。
- 署名だけでなく、
iss、aud、exp、nonce、スコープを用途に応じて検証しているか。 - トークン、認可コード、クライアントシークレットをログへ記録しない設定になっているか。
- 鍵の更新、トークンの期限切れ、失効、ログアウトをテストしているか。
設計判断の結論
利用者のログインにはOIDCのIDトークン、APIの呼び出しにはOAuth 2.0のアクセストークンを使います。
CognitoとAuth0のどちらを選ぶ場合も、製品の設定画面から始めるのではなく、利用者、クライアント、IdP、APIの役割と信頼関係を図にしてから設定へ落とし込むと、トークンの取り違えを防ぎやすくなります。
この記事に関連する株式会社greedenの取り組み
認証基盤は、仕様を知るだけでは安全に運用できず、利用者、API、保守体制に合わせた設計が必要です。株式会社greedenは、要件定義から実装、テスト、保守まで、Webシステム開発を一気通貫で支援しています。
