認証・認可の仕組みである OAuth や OIDC (OpenID Connect) まわりは何かと説明する機会が多いので、ポイントを絞ってまとめておきます。 これを理解すればわかった気になれるという説明です。 サービスの全体構成を考えるときなどに使える一枚絵ができるといいなぁ。
認証と認可
基本をおさらい。
- 認証 (Authentication)
- ユーザーが誰であるかを確認するプロセスです。 例えば、ユーザー名とパスワードを使ってログインすることが認証の一例です。
- 認可 (Authorization)
- 認証されたユーザーが、クライアント(アプリケーション)に対して、どのユーザーリソースにアクセスできるか、どの操作を実行できるかを決定するプロセスです。 勘違いされがちですが、認可の対象はあくまでクライアント(アプリケーション)であり、ユーザーではありません。 ユーザーが、第三者のアプリに対して自分のデータにアクセスしてよいと許可を与えることです。
OAuth と OIDC
OAuth
2012 年に RFC 6749 として OAuth 2.0 が公開されました。 「RFC むなしく」と覚えます(覚えなくてもよいけど)。
OAuth は認可のための HTTP ベースのフレームワーク で、ユーザーのリソース(データ)へのアクセス権を、特定のアプリケーションに付与するための仕組みを提供します。
例えば、Google カレンダーの情報を扱うアプリを作ったとすると、ユーザーは Google の認可画面で「このアプリに自分のカレンダーデータへのアクセスを許可する」ことに同意します。 構図としては、ユーザー自体にアクセス権限を与えているわけではなく、アプリ(OAuth 用語ではクライアント)にリソースへのアクセス権を委譲している という点を理解してください(混同しがちなところ)。 結果として、そのアプリはユーザーの代わりに Google Calendar API などを呼び出すためのアクセストークンを取得し、許可された範囲(スコープ)内でデータにアクセスできるようになります。
OIDC (OpenID Connect)
2014 年に OpenID Foundation が OpenID Connect Core 1.0 を公開しました。
OAuth 2.0 は本来「認可(authorization)」のためのフレームワークであり、ユーザー認証(authentication)を直接目的とした仕様ではありません。 しかし実際には、アクセストークンやスコープを流用して「ログイン」のような用途で使われることが多くありました。
OpenID Connect(OIDC)は、この「OAuth 2.0 の上で動作する認証レイヤー」 として定義された仕様です。
具体的には、ID トークンという JWT 形式のトークンや、ユーザー情報(クレーム)を取得するためのエンドポイント (/userinfo)、およびクレームの標準的な定義などを追加しています。
OpenID Connect (OIDC) は OAuth 2.0 を拡張する形で設計されているため、OIDC を利用するということは、基盤として OAuth 2.0 も同時に利用していることになります。
登場人物と用語
下記は OAuth 2.0 の文脈で出てくる 4 つの構成要素です。 RFC 6749 上では Role(役割)として定義されています。 設計議論をするときは、左端の「通称」を使って話しておけば伝わるはずです。
| 通称 | OAuth 用語 | OIDC 用語 | 説明 |
|---|---|---|---|
| ユーザー | Resource Owner / End User | User / Person | アプリの利用者。参照しようとしているユーザーリソースの持ち主なので、OAuth 2.0 では Resource Owner と呼んでいます。 |
| リソースサーバー | Resource Server | Resource Server / UserInfo Endpoint | ユーザーのデータを保持しているサーバー。アクセスには認可サーバー (IdP) が発行するアクセストークン※1が必要。OIDC ではユーザー情報を提供するためのエンドポイントも定義しています。 |
| クライアント | Client | Relying Party (RP) / Client | アクセストークンを使ってユーザーのリソースにアクセスするアプリ。クラウド上で動作するロジックでも、クライアントと呼ぶのでお気をつけください。あくまで、リソースサーバーに対してのクライアントということを表現しています。 |
| 認可サーバー (IdP: Identity Provider) | Authorization Server | OpenID Provider (OP) / IdP | ユーザー認証機能の提供や、アクセストークン※1の発行を行うサーバー。実体としてはリソースサーバーと同じサーバーでもよい。 |
※1 … 実際には複数のトークンがありますが、ここでの説明では代表的なトークンである「アクセストークン」という単語でまとめています。
OAuth による認可フローの種類
認可フローというのは、クライアント(アプリ)が IdP からアクセストークンを取得するまでの手順のことです。 OAuth 2.0 (RFC 6749) では、下記の 4 つの認可フローを定義しています。 一番重要かつ理解しておきたいのは、1 つ目の 認可コード を使うフローです。
| 認可フローの種類 | 説明 |
|---|---|
| 認可コード (Authorization Code Grant) | IdP が生成したランダムな「認可コード」を使って各種トークンを取得するフローです。クライアント(アプリ)はブラウザからのリダイレクトによって認可コードを受け取り、それを使ってアクセストークンを取得します。OAuth の中で 最も一般的かつ重要なフロー です。 |
| インプリシット (Implicit Grant) | 認可エンドポイントが(認可コードではなく)アクセストークン自体を発行するフローです。リダイレクト先の URL にアクセストークンが付加されてしまう(クライアントではなくユーザーにも見えてしまう)というセキュリティ上の懸念があり、現在は 非推奨 (deprecated) となっています。 |
| リソースオーナー・パスワード・クレデンシャルズ (Resource Owner Password Credentials Grant) | ユーザー (Resource Owner) が、自らの認証情報(ユーザー名・パスワードなど)をクライアントに直接入力し、クライアントがそれをそのまま認可サーバーに送ってトークンを取得するフローです。第三者のクライアント(アプリ)にパスワードが渡されてしまうという問題があり、現在は 非推奨 (deprecated) となっています。 |
| クライアント・クレデンシャルズ (Client Credentials Grant) | クライアント(アプリ)が自らのクレデンシャル(例:client_id + client_secret、あるいは API キーなど)を使って、自分自身の名義でアクセストークンを取得するフローです。ユーザー (Resource Owner) の認証・同意は介さず、トークンは「ユーザーの権限」ではなく「クライアント自体の権限」を表します。主にバックエンドサーバー間での連携 に使われます。 |
認可コードフロー (Authorization Code Flow)
認可コードフロー (Authorization Code Flow) は、OAuth 2.0 における最も一般的な認可フローであり、ほぼすべての登場人物が含まれています。 そのため、この認可コードフローの仕組みを理解できれば、「OAuth 知ってるよ」と言っていいと思います。 ポイントは、クライアント(アプリ)がブラウザ経由で一時的な「認可コード」を受け取って、そのコードを使って最終的なアクセストークンを取得する という流れです。
① [認可リクエスト]まず、ユーザーはアプリからの指示によりブラウザ(Chromeなど)を起動し、サインインと認可を行います。
例えば、自分の Google カレンダーの情報を第三者のアプリに参照させてもよいよ、という同意をします。
このとき使うサインイン画面は、認可サーバー(Googleなど)が提供するものを使うのが一般的です(ユーザーは見慣れた画面でサインインできます)。
② ユーザーによる認可が終わると、その証明である「認可コード」が HTTP のリダイレクトレスポンスとしてブラウザに返されます(URL のクエリパラメータに code が付加される)。
③ リダイレクト URL はクライアントを起動するものになっているため、クライアントは「認可コード」を受け取ることができます。
④ [トークンリクエスト]クライアントは、受け取った「認可コード」を使って、認可サーバーにアクセストークンを要求します。
⑤ 認可サーバーは、クライアントからのリクエストを検証した上で、アクセストークンを発行します。
⑥ クライアントは、取得したアクセストークンを使って、リソースサーバーにアクセスし、ユーザーのデータを取得します。
このような一見まわりくどい構成にすることで、認証・認可処理をブラウザ (User-agent) と認可サーバー (IdP) の間だけで完結することができる ため、ユーザーのパスワードをクライアント(アプリ)に渡す必要がありません(図の①②の部分)。 また、アクセストークンの取得処理がクライアント(アプリ)側に閉じる ため、ブラウザの URL などからトークンが漏れてしまうといったリスクを避けることができます(図の④⑤の部分)。 アクセストークンは基本的にはユーザーにさえ見えず、クライアント(アプリ)だけが参照できます。
つまり、「認証・認可の処理を行う世界」と、「アクセストークンを取得する世界」を分離することにより、セキュリティ上のリスクを低減しているのです。 よく考えられていますね。 ここで勘の良い方は気付いたかもしれませんが、これらの世界をつなぐための「認可コード」という仕組み(図の②③④あたり)に抜け穴はないのかということです。 そこで必要になってくるのが、クライアント認証です。
クライアント認証(クライアントの信頼性を確認する)
認可サーバー (IdP) は、アクセスしてくるクライアント(アプリ)が正規のクライアントかどうかを検証した上で、アクセストークンを発行する必要があります。 これを「クライアント認証」と呼びます。 クライアントの信頼性を確認してからアクセストークンを発行しないと、悪意のあるクライアントにアクセストークンを渡してしまう危険性があります。 例えば、正規のクライアントのリダイレクト URL を偽装して認可コードを取得し、認可サーバーからアクセストークンを取得する、といった攻撃が考えられます。
クライアントの種類
前述の認可コードフローの図では、クライアント(アプリ)を 1 つの青色の箱でシンプルに表現していますが、実際には、サーバー上で動くアプリであったり、ブラウザ上(クライアントサイド JavaScript)で動くアプリであったりします。 OAuth 2.0 では、クライアントを「資格情報を安全に保持できるか」で次の 2 種類に分けており、それぞれクライアント認証の方法が異なります。
- コンフィデンシャル・クライアント (Confidential Client)
- クライアント・シークレットを安全に保持できるクライアント。 一般的にはクラウド上で動作するアプリケーションです(例:サーバーサイドアプリ、BFF (Backend For Frontend)。
- パブリック・クライアント (Public Client)
- クライアント・シークレットを安全に保持できないクライアント。 一般的には端末側で動作するアプリケーションです(例:ブラウザ上で動くクライアントサイド JavaScript や、Android/iOS/Windows/macOS/Linux などのネイティブアプリ)。
コンフィデンシャル・クライアントの検証方法
コンフィデンシャル・クライアントの場合は、クライアント・シークレット を使ったクライアント認証を行います。 簡単にいうと、クライアントと認可サーバー (IdP) の間で共有しておく秘密のパスワードです。 クライアント・シークレットは、認可サーバーの Web フォーム上でコンフィデンシャル・クライアントを登録するときに発行されるのが一般的です(クライアント ID とクライアント・シークレットのペアを生成します)。
サーバーサイドで動作するコンフィデンシャル・クライアントであれば、環境変数などにクライアント・シークレットを登録しておくことができ、認可サーバーとの通信時にそれを送るだけで自分が正規のクライアントであることを証明できます。
具体的には、client_id と client_secret のペアを HTTP のリクエストボディに含めて送信するか、Basic 認証のヘッダー情報として送信します。
認可サーバーは、コンフィデンシャル・クライアントからのアクセス時には必ずクライアント・シークレットの値を確認することで、正しいクライアントのみにアクセストークンを発行することができます。
パブリック・クライアントの検証方法
一方、クライアントサイド JavaScript (SPA: Single Page Application) や、ネイティブアプリのような端末側で動作するパブリック・クライアントでは、クライアント・シークレットを使って自分が正規のクライアントであることを証明することができません。 なぜなら、クライアント・シークレットを JavaScript コードに埋め込んでしまうと、ブラウザの開発者ツールなどで簡単に参照できてしまい、悪意のあるクライアントに悪用されてしまうため、意味がないからです。
つまり、認可サーバーは、パブリック・クライアントが正規のクライアントだと仮定してアクセストークンを発行するしかありません。 認可コードフローの途中で、何らかの形で「認可コード」が悪意のあるクライアントに傍受されてしまうと、それはすなわちアクセストークンを取得されてしまうことを意味します。 そこで考えられた対応策が、PKCE (Proof Key for Code Exchange)(ピクシー) という OAuth 2.0 の拡張仕様です。
認可サーバーにクライアントを登録する際は、クライアントの種類(コンフィデンシャル・クライアントかパブリック・クライアントか)を指定する必要があります。 これは、上記のように、クライアント認証の方法が異なるためです。 例えば、コンフィデンシャル・クライアントとして登録された場合は、認可サーバーはトークンリクエスト時にクライアント・シークレットの確認を行います。 パブリック・クライアントとして登録された場合は、クライアント・シークレットの確認は行わず、PKCE を使った検証を行います。
さらに、認可コードフローで使うためのリダイレクト URI をあらかじめ登録しておく必要がありますが、これは、悪意のあるクライアントに認可コードが転送されてしまうことを防ぐための仕組みです。 しかし、リダイレクト URI の登録だけでは、悪意のあるクライアントが正規のクライアントのリダイレクト URI を偽装して認可コードを取得することを防ぐことはできません。 また、クライアントが Android などのネイティブアプリである場合は、リダイレクト URI に独自のスキームを設定することがあり、リダイレクト時に悪意のあるクライアントが起動してしまう可能性が高くなります(参考:RFC 8252: OAuth 2.0 for Native Apps)。
PKCE (Proof Key for Code Exchange)
パブリック・クライアントを用いた構成では、PKCE (Proof Key for Code Exchange)(ピクシー) という OAuth 2.0 拡張を組み合わせて使うことで、正しいクライアントからのアクセストークン要求であることを判断します。 認可コードフローの中で、悪意のあるクライアントに「認可コード」が途中で傍受されてしまった場合に、アクセストークンの発行を止めるための仕組みです。 2026 年現在ドラフト段階である OAuth 2.1 では、認可コードフローを使うすべてのクライアントに対して PKCE の使用が必須とされています(パブリック・クライアントに限りません)。
PKCE の考え方はシンプルで、「認可リクエストを出したクライアント」と「アクセストークンリクエストを出したクライアント」が同一であることを確認する、というものです。
正規のクライアントは、認可リクエストを出す前に、自分しか知り得ないランダムな code_verifier を生成して内部で保持しておきます(①)。
認可リクエストでは、これを SHA-256 でハッシュ化した値 code_challenge を送信します(②)。
認可サーバーは、認可リクエストを受け取ったときに、code_challenge を保持しておきます(③) 。
正規のクライアントがアクセストークンリクエストを出すときに、code_verifier を送信します(⑤)。
認可サーバーは、受け取った code_verifier をハッシュ化して、保持していた code_challenge と一致するかを検証します(⑥)。
これにより、仮に悪意のあるクライアントが「認可コード」を傍受したとしても、code_verifier を知らない限り、アクセストークンを取得することはできません。
現在 OAuth 2.0 でパブリック・クライアントによる認可を行う場合は、この「認可コードフロー + PKCE」という構成が推奨されています。
Device flow(デバイスフロー)
Device Authorization Grant(デバイス認可グラント)、通称「デバイスフロー」 とは、テレビやオーディオ機器などの入力手段が乏しいデバイスで OAuth 2.0 を使った認可を行うための拡張仕様です。 RFC 6749 (OAuth 2.0) では、アプリケーション(クライアント)を使う端末にインストールされたブラウザ (user-agent) を使って認可を行うフローが前提となっていますが、テレビなどのデバイスでは、ブラウザが使えなかったり、ユーザーが文字入力を行うのが困難な場合があります。 デバイスフローでは、ユーザーが別のデバイス(スマートフォンや PC など)を使って認可を行うことができます。 逆に、テレビ側のブラウザは使わず、ユーザーがスマートフォンや PC で認可を行うための URL とコードを表示するだけです。
デバイスフローでは、認可リクエストを受け付けるエンドポイントとして、Device Authorization Endpoint を定義しています。 トークンリクエストには、通常の認可コードフローと同様の Token Endpoint を使います。
デバイスフローは認可コードフローとは独立したグラント種別(grant_type=urn:ietf:params:oauth:grant-type:device_code)です。
認可コードのリダイレクトを行わず、クライアントがトークンエンドポイントをポーリングする方式のため、PKCE は適用されません。
セキュリティ上の保護は、device_code の秘匿、user_code の短い有効期限、およびポーリングのレート制限によって担保されます。
ネイティブアプリの場合
RFC 8252 では、ネイティブアプリから OAuth 2.0 を使う場合のベストプラクティス を定義しています。 ネイティブアプリというのは、スマホアプリ (iOS/Android) や、PC 用のアプリ (Windows/macOS/Linux) など、端末側の OS 上で動作するアプリケーションのことを指します。 これらのネイティブアプリが認可リクエストを出すときは、アプリ外部の User-Agent(つまり Web ブラウザ)を使う ことになります。
理屈的には、ネイティブアプリに組み込まれた WebView などのブラウザーコンポーネント(Embedded user-agent と呼びます)を使って認可リクエストを出すことも可能ですが、セキュリティ上の懸念があるため推奨されていません。 アプリ内部で直接認可処理を行わず、アプリ外部のブラウザを使用するのには以下のような理由があります。
- アプリ(クライアント)から認証・認可のやりとりを覗き見られないようにする。
- アプリの UI 上に悪意のある透明なウィンドウ (iframe) をかぶせられるといった攻撃(クリックジャッキング)を防ぐ。
- ブラウザにセッション情報が残っていれば、ユーザーは再度ログインすることなく認可を行える (Single Sign-On)。
アプリ外部のブラウザを使うといっても、OS によってはアプリと一体化されているように見せることができます。 例えば、Android では Custom Tabs プロトコルを実装したブラウザ(Chrome など)であれば、In-App Browser Tab としてアプリに組み込む形で認可 UI を表示することができます。
OIDC (OpenID Connect)
…
その他/補足情報
RFCs
代表的な IdP
- Amazon Cognito … AWS で提供されている IDaaS
- Keycloak … オープンソースの IdP
- Okta … 商用 IDaaS(企業向け)
- Auth0 … 商用 IDaaS(開発者向け)(2021 年に Okta が買収)
- Authlete … IdP を実装するための API を提供




