---
title: "OIDCの認証フローとは？3つのフローとresponse_typeの対応・移行判断を実装視点で解説"
url: "https://www.issoh.co.jp/tech/details/19354/"
published: 2026-11-05
updated: 2026-11-05
categories: ["セキュリティ"]
publisher: "株式会社一創"
---

# OIDCの認証フローとは？3つのフローとresponse\_typeの対応・移行判断を実装視点で解説

OIDC（OpenID Connect）の認証フローは、認可リクエストに載せる`response_type`の値で決まります。仕様が定める組み合わせは6種類で、それぞれの分類先は認可コードフロー・インプリシットフロー・ハイブリッドフローのいずれかです。本記事では、3つのフローの違いをトークンの受け取り窓口で整理したうえで、フローごとに変わる`nonce`・`c_hash`・`at_hash`の検証、`response_mode`の既定値、IdPが実際に受け付ける組み合わせを確かめる手順を解説します。インプリシットから認可コード＋PKCEへ移行するときは、既存システムに残してよい条件と新規採用しない場面を分け、その判断基準も最後に示します。

## まとめ：OIDCの3つの認証フローで新規実装が選ぶべき組み合わせ

新規に作るログイン連携は、`response_type=code`の認可コードフローにPKCEを付ける構成だけを選びます。サーバサイドWeb、SPA、モバイルアプリのどれであっても結論は変わりません。トークンはすべてトークンエンドポイントから受け取り、ブラウザのURLには短命の認可コードしか載せない形です。

インプリシットフロー（`id_token`／`id_token token`）は新規には採りません。OAuth 2.0のセキュリティBCPであるRFC 9700と、Microsoft Entraの公式ドキュメントがそろって使わないよう求めています。既存のSPAがインプリシットで動いているなら、移行の予定を立てる対象です。

ハイブリッドフロー（`code id_token`など3種）は、認可応答の時点でIDトークンが要る既存システムに限って残してよい選択肢です。残すなら`c_hash`の照合と`form_post`での受け取りを必ずセットにします。

## OIDCの認証フロー3種をresponse\_typeの値で見分ける仕様の読み方

最初に、フロー名とパラメータ値の対応を仕様の原文で固定します。用語だけで覚えると、IdPの設定画面とリクエストの値が結び付きません。

### Core 1.0のセクション3が定める6つのresponse\_typeとフローの対応表

[OpenID Connect Core 1.0（errata set 2を反映した版）](https://openid.net/specs/openid-connect-core-1%5F0.html)のセクション3は、`response_type`の値とフローを次のように対応させています。

| response\_type       | フロー     | 認可応答に載るもの       |
| -------------------- | ------- | --------------- |
| code                 | 認可コード   | 認可コードのみ         |
| id\_token            | インプリシット | IDトークン          |
| id\_token token      | インプリシット | IDトークンとアクセストークン |
| code id\_token       | ハイブリッド  | 認可コードとIDトークン    |
| code token           | ハイブリッド  | 認可コードとアクセストークン  |
| code id\_token token | ハイブリッド  | 3つすべて           |

OAuth 2.0のインプリシットグラントで使う`token`単独は、この表にありません。IDトークンが返らないため、OIDCとしてはログインを成立させられないからです。空白区切りの組み合わせという書き方そのものは、[OAuth 2.0 Multiple Response Type Encoding Practices](https://openid.net/specs/oauth-v2-multiple-response-types-1%5F0.html)が定義しています。値の並び順は意味を持たず、`id_token code`と書いても`code id_token`と同じ扱いです。

### トークンを返す窓口の違いで分かれる3フローの性質7項目の比較

3つのフローの差は、トークンを認可エンドポイントとトークンエンドポイントのどちらから受け取るかに集約されます。Core 1.0のセクション3にある比較表を、実装判断に効く順に並べ直しました。

| 性質                  | 認可コード | インプリシット | ハイブリッド |
| ------------------- | ----- | ------- | ------ |
| トークンがブラウザに露出しない     | はい    | いいえ     | いいえ    |
| クライアントを認証できる        | はい    | いいえ     | はい     |
| リフレッシュトークンを得られる     | はい    | いいえ     | はい     |
| 全トークンを認可エンドポイントから   | いいえ   | はい      | いいえ    |
| 全トークンをトークンエンドポイントから | はい    | いいえ     | いいえ    |
| 1往復で完結する            | いいえ   | はい      | いいえ    |
| 大半がサーバ間通信           | はい    | いいえ     | 場合による  |

見るべきは上の3行です。インプリシットは「1往復で完結する」以外のすべてで不利になります。かつてはSPAがトークンエンドポイントへクロスオリジンで通信できないという事情があり、この1往復の利点が選ばれる理由でした。今はMicrosoft EntraのようにSPA向けの登録種別でトークンエンドポイントへのクロスオリジン通信を許すIdPがあり、その前提は崩れています。

### response\_modeの既定値とform\_postでフラグメントを避ける受け取り方

認可応答をどこに載せて返すかは`response_mode`で決まります。指定しない場合の既定値は`response_type`で変わり、`code`はクエリ文字列、`id_token`または`token`を含む値はURLフラグメント（`#`以降）です。Core 1.0の3.2.2.5も、インプリシットの応答をフラグメントに載せると定めています。

フラグメントはサーバへ送られないため、受け取るにはブラウザ側のJavaScriptで読むしかありません。履歴やブラウザ拡張から見える位置にトークンが残る点も弱みです。そこで使うのが[OAuth 2.0 Form Post Response Mode](https://openid.net/specs/oauth-v2-form-post-response-mode-1%5F0.html)の`response_mode=form_post`で、IdPは自動送信するHTMLフォームを返し、応答値をフォームの本文として`redirect_uri`へPOSTさせます。ハイブリッドやインプリシットを残すなら、受け取りは`form_post`に寄せてください。

## 認可コードフローでOIDCログインを組むときにOAuthへ足す処理

認可コードフローの手順そのものはOAuth 2.0と共通です。`redirect_uri`の登録や`state`、コードの一回性は[認可リクエストからトークン交換までの実装手順](https://www.issoh.co.jp/tech/details/16415/)で扱っているので、ここではOIDCとして追加する差分だけを書きます。

### openidスコープとnonceを付けた認可リクエストの組み立て例

OIDCのログインにするための条件は、`scope`に`openid`を含めることです。これが無いと認可サーバはIDトークンを発行せず、単なるOAuthの認可になります。認可コードフローでの`nonce`は仕様上は任意ですが、送っておけばIDトークンのリプレイ検出に使えます。

```
GET /authorize?response_type=code
  &client_id=example-client
  &redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback
  &scope=openid%20profile%20email
  &state=af0ifjsldkj
  &nonce=n-0S6_WzA2Mj
  &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
  &code_challenge_method=S256 HTTP/1.1
Host: idp.example.com
```

`code_challenge`は[RFC 7636](https://www.rfc-editor.org/rfc/rfc7636.html)が定めるPKCEのパラメータで、上の値はRFC 7636の付録Bにある計算例です。`code_verifier`の生成と保存先の設計は[PKCEのcode\_verifier実装と検証設計](https://www.issoh.co.jp/tech/details/16405/)に分けています。

### トークン応答のid\_tokenを検証してからセッションを発行する順序

トークンエンドポイントの応答には、`access_token`と並んで`id_token`が入ります。ログイン判定に使うのはこの`id_token`で、アクセストークンをログインの証拠にしてはいけません。アクセストークンは「このAPIを呼んでよい」という許可であり、誰がどのクライアントにログインしたかを保証しないからです。

処理の順序は、署名検証、`iss`・`aud`・`exp`の照合、送った`nonce`との一致確認を済ませてから、アプリのセッションを発行します。検証項目の全体は[IDトークンの検証手順とOAuth 2.0との違い](https://www.issoh.co.jp/tech/details/6532/)で解説しています。

## インプリシットフローの仕組みとnonce必須・リフレッシュトークン不可の制約

インプリシットは、認可エンドポイントへの1回のリダイレクトでIDトークンまで受け取るフローです。速さの代償として、検証の負担はすべてクライアント側に乗ります。

### id\_tokenとid\_token tokenの2種類で変わるat\_hashの扱い

インプリシットでは`nonce`が必須になります（Core 1.0の3.2.2.1と3.2.2.10）。IDトークンがブラウザ経由で届くため、盗まれたトークンの使い回しを、セッションに保存した`nonce`との照合で止めるしかないからです。

`id_token token`でアクセストークンも受け取る場合、IDトークンには`at_hash`が必須で入ります。`at_hash`はアクセストークンのハッシュ値の左半分で、IDトークンの署名がそのアクセストークンと対であることを示します。`id_token`単独ならアクセストークンが無いため`at_hash`も入りません。リフレッシュトークンはどちらでも発行されず、期限が切れたら再度リダイレクトさせる設計になります。

### RFC 9700とMicrosoft Entraがインプリシットを使わないよう求める根拠

[RFC 9700（Best Current Practice for OAuth 2.0 Security、2025年1月発行）](https://www.rfc-editor.org/rfc/rfc9700.html)の2.1.2は、クライアントは`token`や、認可応答でアクセストークンを返すその他のレスポンスタイプを使うべきでない（SHOULD NOT）と書いています。代わりに挙げているのは`code`と、トークンをトークン応答で返す`code id_token`です。つまり`id_token token`と`code token`は推奨から外れ、ハイブリッドのうち`code id_token`だけが名指しで許容されています。

運用面の理由もあります。[Microsoft Learnの暗黙的な許可のフロー](https://learn.microsoft.com/ja-jp/entra/identity-platform/v2-oauth2-implicit-grant-flow)によれば、サードパーティCookieを削除するブラウザでは非表示iframeによるサイレントSSOが動きません。リフレッシュトークンも無いので、ユーザーは頻繁に再ログインさせられる。問題はまず使い勝手の崩れとして表に出ます。

## ハイブリッドフローの3つの組み合わせとc\_hash・at\_hashの検証手順

ハイブリッドは、認可コードとIDトークン（またはアクセストークン）を認可応答で同時に受け取るフローです。検証の手順が最も多く、実装の抜けが起きやすい形でもあります。

### code id\_tokenで認可応答の時点にユーザーを特定する使い道

`code id_token`を使う理由は、トークン交換の前にユーザーを特定できる点にあります。認可応答に入ったIDトークンで「誰が戻ってきたか」を確定し、認可コードはバックエンドで交換してアクセストークンとリフレッシュトークンを得る。表示の分岐をトークン交換より先に決めたい画面や、OIDCの初期に作られたWebアプリで見かける構成です。

3種のうち`nonce`が必須なのは`code id_token`と`code id_token token`で、`code token`では任意です（Core 1.0の3.3.2.1）。`c_hash`は`code id_token`と`code id_token token`で必須、`at_hash`は`code id_token token`で必須になります（3.3.2.11）。

### Node.jsによるc\_hash・at\_hash計算と認可コード差し替えの検出例

`c_hash`は、認可応答の`code`が、同じ応答のIDトークンと一緒に発行されたものかを確かめる値です。攻撃者が別のセッションで得た認可コードに差し替えても、IDトークンの署名は書き換えられないため、ハッシュが合わなくなります。計算方法はCore 1.0の3.3.2.11が定めており、IDトークンの`alg`に対応するハッシュ関数（RS256ならSHA-256）で値をハッシュし、左半分をbase64urlで符号化します。

```
// hybrid-hash.mjs（Node.js 18以降。外部パッケージ不要）
import { createHash } from 'node:crypto';

// IDトークンの alg からハッシュ長を決め、左半分を base64url にする
function halfHash(value, alg) {
  const bits = alg.slice(2); // 'RS256' -> '256'、'ES384' -> '384'
  if (!['256', '384', '512'].includes(bits)) {
    throw new Error(`unsupported alg: ${alg}`);
  }
  const digest = createHash(`sha${bits}`).update(value, 'ascii').digest();
  return digest.subarray(0, digest.length / 2).toString('base64url');
}

// 署名検証済みのクレームに対して c_hash と at_hash を照合する
export function verifyHybridResponse({ responseType, code, accessToken, claims, alg }) {
  const types = new Set(responseType.split(' '));
  if (types.has('id_token') && claims.c_hash !== halfHash(code, alg)) {
    throw new Error('c_hash mismatch');
  }
  const atRequired = types.has('id_token') && types.has('token');
  if (atRequired || (accessToken && claims.at_hash)) {
    if (claims.at_hash !== halfHash(accessToken, alg)) {
      throw new Error('at_hash mismatch');
    }
  }
}

// 動作確認：同じ入力なら毎回同じ22文字が出る
console.log(halfHash('SplxlOBeZQQYbYS6WxSbIA', 'RS256'));
```

呼び出す前提は、IDトークンの署名検証と`iss`・`aud`・`nonce`の照合が済んでいることです。署名を確かめていないクレームと比べても、攻撃者が`c_hash`ごと作り直せるため意味がありません。

### 2枚のIDトークンでissとsubが一致するかを照合する実装の要点

`code id_token`と`code id_token token`では、IDトークンが2回届きます。1枚目は認可応答から、2枚目は認可コードを交換したトークン応答からです。Core 1.0は、この2枚で`iss`と`sub`が同一でなければならないと定めています。

実装では、1枚目の`sub`をセッションに一時保存し、トークン交換後に2枚目と突き合わせます。食い違ったらログインを中断し、どちらの値も採用しません。プロフィール情報は2枚目から取り、本人の同定は両方の一致で行う、と役割を分けておきます。

## IdPのDiscovery文書で対応フローを確認してから設計する手順

仕様が6種を定めていても、IdPがすべてを受け付けるとは限りません。設計の前に、使うIdPの実際の対応値を確認します。

### GoogleとMicrosoft Entraの対応するレスポンスタイプ実測値の比較

[OpenID Connect Discovery 1.0](https://openid.net/specs/openid-connect-discovery-1%5F0.html)は、IdPが`/.well-known/openid-configuration`で対応機能を公開する形式を定めています。2026年11月5日時点で取得した値は次のとおりでした（項目名の末尾「\_supported」は省略）。

| 項目              | Google                    | Microsoft Entra（common）   |
| --------------- | ------------------------- | ------------------------- |
| response\_types | 8種（code token も含む）        | 4種（code token は無し）        |
| response\_modes | query／fragment／form\_post | query／fragment／form\_post |
| PKCE方式の記載       | plain／S256                | 記載なし                      |

Googleは[openid-configuration](https://accounts.google.com/.well-known/openid-configuration)で`code token`や`none`まで8種を列挙しています。[Microsoft Entraのcommonエンドポイント](https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration)は4種で、`code token`と`code id_token token`はありません。Entraでは`code_challenge_methods_supported`が記載されていない一方、PKCEはドキュメント上で使えます。Discoveryの記載漏れを非対応と読み違えないよう、公式ドキュメントと照らし合わせてください。

### curl・jqによるresponse\_types・response\_modes確認例

確認は1コマンドで済みます。IdPの選定段階で候補ごとに実行し、結果を設計書に残します。

```
curl -s https://accounts.google.com/.well-known/openid-configuration \
  | jq '{response_types_supported, response_modes_supported, code_challenge_methods_supported}'

# 社内IdP（Keycloakなど）の場合は issuer の末尾に付ける
curl -s https://idp.example.com/realms/main/.well-known/openid-configuration \
  | jq '.response_types_supported'
```

見るべきは、移行元で使っている組み合わせが残っているか、`form_post`が使えるか、`S256`が載っているかの3点です。

## インプリシットから認可コード＋PKCEへ移行する判断と見送る場面

ここからは判断の話です。結論として、インプリシットで動いている画面は移行の対象にし、ハイブリッドは条件を満たす間だけ残します。

### SPAの移行で変わるredirect\_uriの種別とトークン保存先の決め方

SPAを認可コード＋PKCEに移すと、IdP側のアプリ登録が変わります。Microsoft Entraでは、リダイレクトURIを「Web」ではなく「シングルページアプリケーション」の種別で登録し直さないと、ブラウザからトークンエンドポイントを呼べません。アプリ側は認可応答からコードを取り出し、`code_verifier`を添えてトークンエンドポイントへPOSTする処理が増えます。

移行で迷うのはトークンの置き場所です。ブラウザ内に置くならメモリ保持を基本にし、localStorageは避けます。XSSが1つ通れば読み出せるためです。扱うデータが機微で、ログイン状態を長く保つ必要があるなら、BFF（Backend for Frontend）を置いてトークンをサーバ側に閉じ込める構成を選びます。社内の認証基盤ごと設計を見直す場合は、一創の[認証基盤・ID管理システム開発](https://www.issoh.co.jp/service/system/identity/)でIdP選定から移行計画まで支援しています。

### 既存システムにハイブリッドを残してよい条件と新規採用しない場面の線引き

既存のハイブリッドを残してよいのは、次の条件をすべて満たす場合です。

- `response_type`が`code id_token`で、アクセストークンを認可応答に含めていない
- 受け取りが`form_post`で、フラグメントにトークンが載らない
- `nonce`・`c_hash`・2枚のIDトークンの`sub`一致をすべて検証している
- 使っているフレームワークの既定がハイブリッドで、変更するとミドルウェアの差し替えが要る

1つでも欠けるなら、残す理由より直す理由のほうが大きくなります。新規の画面では、たとえ同じフレームワークでも`code`単独に設定を変えて作ってください。OAuth 2.1の草案（[draft-ietf-oauth-v2-1](https://datatracker.ietf.org/doc/draft-ietf-oauth-v2-1/)、2026年10月31日更新の17版）はインプリシットを本文から外し、PKCEを全クライアントの前提としています。標準の向かう先に、ハイブリッドを新しく採る理由はありません。

### 移行レビューで差し戻す項目：nonce未検証とフラグメント受け取りの残存

移行のプルリクエストで見落としが多いのは、新しい処理を足して古い処理を消し忘れるパターンです。レビューでは次の3点を確認し、該当すれば差し戻します。

1. コールバック処理に`location.hash`を読むコードが残っている。インプリシット用の受け口が生きていれば、攻撃者はそちらへ誘導できる
2. 認可コードフローに移したことを理由に`nonce`の照合を消している。任意になっただけで、送っているなら照合する
3. IdP側で暗黙的な許可（Entraなら「暗黙の付与およびハイブリッド フロー」のIDトークン・アクセストークン）が有効のまま残っている

3つ目はアプリのコードに現れないため、IdPの管理画面まで確認範囲に入れます。LINEログインのように、IdP固有のIDトークン検証APIを併用している場合の実装例は[LINEログイン APIの実装手順：PKCE・IDトークン検証と既存会員IDの連携](https://www.issoh.co.jp/tech/details/19337/)が参考になります。

## よくある質問

OIDCの認証フローについて、実装の相談でよく受ける質問をまとめました。

### OIDCとOAuth 2.0で認可コードフローの手順は違いますか？

手順の骨格は同じです。違いは、`scope`に`openid`を含めること、トークン応答に`id_token`が加わること、その`id_token`を検証してからログインを成立させることの3点です。`nonce`は認可コードフローでは任意ですが、送れば照合に使えます。OAuth 2.0側のグラント種別の全体像は[OAuth 2.0の仕組みと認可フロー](https://www.issoh.co.jp/tech/details/8369/)で整理しています。

### インプリシットフローは今でも使えますか？

多くのIdPはまだ受け付けますが、新規には使いません。RFC 9700が認可応答でアクセストークンを返すレスポンスタイプを使わないよう求め、Microsoft Entraはサードパーティ Cookie を削除するブラウザでサイレントSSOが動かないと明記しています。リフレッシュトークンも得られないため、既存の画面も認可コード＋PKCEへの移行を計画してください。

### ハイブリッドフローはどんなときに使いますか？

トークン交換の前に、認可応答の時点でユーザーを特定したいときです。主に`code id_token`を既定にしてきたフレームワークの既存システムで見かけます。使うなら`form_post`で受け取り、`nonce`と`c_hash`、2枚のIDトークンの`iss`・`sub`一致を検証してください。新規の画面では`code`単独で足ります。

### response\_typeとgrant\_typeは何が違いますか？

`response_type`は認可エンドポイントへのリクエストで「何を返してほしいか」を指定する値です。`grant_type`はトークンエンドポイントへのリクエストで「何と引き換えにトークンを出してほしいか」を示し、認可コードフローなら`authorization_code`を送ります。前者はブラウザのリダイレクト、後者はサーバまたはアプリからのPOSTに載ります。

### 使うIdPがどのフローに対応しているかはどう確認しますか？

IdPの`/.well-known/openid-configuration`を取得し、`response_types_supported`と`response_modes_supported`を見ます。2026年11月時点ではGoogleが8種、Microsoft Entraのcommonエンドポイントが4種を列挙していました。記載が無い項目でも対応している場合があるため、PKCEのように重要な機能は公式ドキュメントでも確認してください。

## 関連記事

- [JWTとは？構造・署名検証の仕組みとセッション・OAuth/OIDCとの違いを実装視点で解説](https://www.issoh.co.jp/tech/details/13453/)：IDトークンの署名検証をライブラリ任せにせず理解したいときに。
- [SAMLとは？認証フロー・IdP/SPの仕組みとOAuth・OIDCとの使い分けを実装視点で解説](https://www.issoh.co.jp/tech/details/13299/)：社内SSOでSAMLとOIDCのどちらを採るか比べるときに。
- [IDaaSとは？SSO・IdPとの違いとSAML・OIDC・SCIM連携を実装視点で解説](https://www.issoh.co.jp/tech/details/16401/)：IdPを自前で持たず外部サービスへ寄せる判断に。
- [認証・ID管理とは？MFA・SSO・OIDC・IDaaSの違いと選定を解説【2026年版】](https://www.issoh.co.jp/column/details/13631/)：認証基盤の見直しを社内で説明する材料として。
- [LINEログイン APIの実装手順：PKCE・IDトークン検証と既存会員IDの連携](https://www.issoh.co.jp/tech/details/19337/)：ソーシャルログインで同じフローを実装する具体例として。

---

出典: [OIDCの認証フローとは？3つのフローとresponse\_typeの対応・移行判断を実装視点で解説](<https://www.issoh.co.jp/tech/details/19354/>)（株式会社一創）
