---
title: "OIDC（OpenID Connect）とは？IDトークンの検証手順とOAuth 2.0との違いを解説"
url: "https://www.issoh.co.jp/tech/details/6532/"
published: 2025-05-05
updated: 2026-09-27
categories: ["プロトコル"]
publisher: "株式会社一創"
---

# OIDC（OpenID Connect）とは？IDトークンの検証手順とOAuth 2.0との違いを解説

OIDC（OpenID Connect、読み方はオープンアイディー・コネクト）は、OAuth 2.0の上に「このユーザーは誰か」を確かめる仕組みを足した認証の仕様です。OAuth 2.0だけでは、アプリは「APIを呼んでよい権限」を受け取れても、ログインした本人が誰かを確実には知れません。OIDCはそこに**IDトークン**という署名付きの本人確認書を加えました。本記事では、IDトークンに入る必須項目と、受け取ったアプリが仕様上どの順で何を検証しなければならないかを、[OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1%5F0.html)の原文にもとづいて整理します。`aud` の意味と、OAuth 2.0・SAMLとの違いも扱います。

## まとめ：OIDCの要点とIDトークン検証で外せない項目

- OIDCは**OAuth 2.0を拡張した認証レイヤー**です。認可リクエストの `scope` に `openid` を入れると、トークンレスポンスにIDトークンが加わります。
- IDトークンはJWTで、`iss`（発行者）・`sub`（ユーザー識別子）・`aud`（宛先のclient\_id）・`exp`（有効期限）・`iat`（発行時刻）の5つが必須です。
- 受け取ったアプリは、`iss` の完全一致、`aud` に自分のclient\_idが含まれること、署名、`exp`、送った `nonce` との一致を検証します（Core 3.1.3.7）。`aud` を見ないと、別アプリ向けに発行されたトークンでログインを通してしまいます。
- ユーザーを識別するキーは `iss` と `sub` の組です。メールアドレスは発行者側で再利用されることがあり、キーにしてはいけません（Core 5.7）。
- フローは認可コードフロー（`response_type=code`）＋PKCEが既定です。認可レスポンスでアクセストークンを返す型は、RFC 9700が非推奨（SHOULD NOT）としています。

## OIDCの定義と仕様の位置づけ

### OAuth 2.0に足されたIDトークンとopenidスコープ

Coreの冒頭は、OIDCを「OAuth 2.0プロトコルの上に載るシンプルなIDレイヤー」と定義しています。最もよく使う認可コードフローで見ると、やり取りの形はOAuth 2.0の認可リクエストとトークンリクエストのままで、違いは2点です。認可リクエストの `scope` に `openid` を含めること、そしてトークンエンドポイントがアクセストークンに加えてIDトークンを返すことです（トークンエンドポイントを使わないインプリシットフローでは、IDトークンは認可レスポンスで返ります）。Coreは「OAuth 2.0を認証に使えるようにする主な拡張がIDトークンというデータ構造である」と書いています。

OAuth 2.0そのものの仕組みとロールは[OAuth 2.0とは？仕組み・認可フローと認証との違い、OAuth 2.1の変更点をわかりやすく解説](/tech/details/8369/)で扱っています。

### OP・RP・エンドユーザーの3者の関係

OIDCの登場人物は3者です。ログインする人が**エンドユーザー**、ユーザーを認証してIDトークンを発行するのが**OpenIDプロバイダー（OP）**、IDトークンを受け取ってログインを成立させるアプリが**リライングパーティー（RP）**です。「Googleでログイン」なら、Googleアカウントの認証基盤がOP、ログインボタンを置いたWebサービスがRPになります。OAuth 2.0の用語に当てはめると、OPは認可サーバー、RPはクライアントです。

OPには、Google・Microsoft Entra ID・Apple・Auth0・Amazon Cognitoなどのクラウドサービスのほか、Keycloakのように自社で立てるソフトウェアもあります。Keycloakのトークンエンドポイントは `/realms/{realm-name}/protocol/openid-connect/token` という形で、パスに `openid-connect` が入ります。

### 仕様の版とISO標準化の時期

| 時期         | 出来事                         |
| ---------- | --------------------------- |
| 2014-02-26 | OpenID Connect 1.0 Final 公開 |
| 2014-11    | Core errata set 1           |
| 2023-12-15 | Core errata set 2（現行版）      |
| 2024-10-07 | 9仕様がISO/IEC標準化と発表           |

現行のCoreは「incorporating errata set 2」の版です。ISO/IEC 26131:2024がCore、26132:2024がDiscovery、26133:2024がDynamic Client Registrationに当たります。実装で参照するのは、openid.netで無償公開されている原文で足ります。

## OAuth 2.0とOIDCの違い：認可と認証

| 観点      | OAuth 2.0       | OIDC                        |
| ------- | --------------- | --------------------------- |
| 目的      | 認可（APIを呼ぶ権限の委譲） | 認証（ログインした本人の確認）             |
| 主な成果物   | アクセストークン        | IDトークン（＋アクセストークン）           |
| トークンの宛先 | リソースサーバー（API）   | クライアント（RP）自身                |
| トークンの形式 | 規定なし            | JWT（原則署名付き）                 |
| 仕様      | RFC 6749（IETF）  | Core 1.0（OpenID Foundation） |

違いの核心は「トークンの宛先」です。アクセストークンはAPIに見せるためのもので、クライアントが中身を読んで解釈することをOAuth 2.0は想定していません。IDトークンはクライアント自身に宛てたもので、`aud` にクライアントのclient\_idが入ります。

### アクセストークンだけでログインを判定する危険

OAuth 2.0だけでログイン機能を作ると、「アクセストークンが取れた＝その人がログインした」とみなしがちです。しかしOAuth 2.0単独では、クライアントがアクセストークンを見て「自分に向けたログイン結果か」を確かめる手順が標準化されていません（JWT形式のアクセストークンを定めたRFC 9068には `client_id` がありますが、これはAPI側が読むための項目です）。攻撃者が自分の運営する別アプリで被害者のアクセストークンを集め、それを標的アプリに差し出すと、トークンの宛先を確かめない標的アプリは被害者としてログインさせてしまいます。IDトークンは `aud` で宛先のアプリを示し、`nonce` でその認証リクエストに結び付けるため、この差し替えを検知できます。OAuth 2.0でログインを組みたくなったら、それはOIDCを使う場面です。

## IDトークンとアクセストークンの違い

| 項目          | IDトークン        | アクセストークン        |
| ----------- | ------------- | --------------- |
| 誰が読むか       | クライアント（RP）    | API（リソースサーバー）   |
| audに入る値     | RPのclient\_id | APIの識別子（JWTの場合） |
| 形式          | 署名付きJWT       | JWTまたは不透明な文字列   |
| API呼び出しに使うか | 使わない          | 使う              |

ログイン用にRPへ発行されたIDトークンを、そのままAPIへのBearerトークンとして流用する実装は誤りです。そのIDトークンの `aud` はRPのclient\_idなので、宛先を検証するAPIなら拒否します。例外は、Google Cloud Runのサービス間認証のように、`aud` に呼び出し先のサービスを指定したIDトークンを発行し、それをBearerで送る方式です。この場合もAPIは `aud` が自分であることを検証しており、ログイン用トークンの流用とは別物です。

### IDトークンに入る必須クレームと条件付きクレーム

Coreの2章が定めるIDトークンの中身は次のとおりです。下のJSONはCore 2章に載っている例です。

```
{
  "iss": "https://server.example.com",
  "sub": "24400320",
  "aud": "s6BhdRkqt3",
  "nonce": "n-0S6_WzA2Mj",
  "exp": 1311281970,
  "iat": 1311280970,
  "auth_time": 1311280969,
  "acr": "urn:mace:incommon:iap:silver"
}
```

| クレーム       | 要否                          | 意味                                    |
| ---------- | --------------------------- | ------------------------------------- |
| iss        | 必須                          | 発行者。httpsのURL                         |
| sub        | 必須                          | 発行者内で一意・再割り当てなしのユーザーID（ASCII 255文字以内） |
| aud        | 必須                          | 宛先。RPのclient\_idを含む                   |
| exp        | 必須                          | 有効期限（UNIX秒）                           |
| iat        | 必須                          | 発行時刻（UNIX秒）                           |
| nonce      | リクエストに含めたら必須                | リクエストとの紐付け・リプレイ対策                     |
| auth\_time | max\_age指定時・Essential要求時は必須 | ユーザーが認証した時刻                           |
| acr / amr  | 任意                          | 認証の強度・使った認証手段                         |
| azp        | 任意                          | トークンを受け取る当事者のclient\_id               |

`exp` はIDトークンの受け入れ期限で、Coreは「RPとOPの間のログインセッションの長さとは無関係」と明記しています。IDトークンが切れたからといってユーザーを即ログアウトさせる必要はなく、アプリのセッションは別に管理します。JWTのヘッダー・署名の構造そのものは[JWTとは？構造・署名検証の仕組みとセッション・OAuth/OIDCとの違いを実装視点で解説](/tech/details/13453/)で扱っています。

## IDトークンの検証手順（Core 3.1.3.7）

### 仕様が定める検証項目と順序

認可コードフローでIDトークンを受け取ったRPは、Core 3.1.3.7に従って次の順に検証します。

1. 暗号化されていれば復号する（登録時に暗号化を取り決めたのに平文なら拒否してよい）
2. OPのIssuer Identifier（通常はDiscoveryで取得）と `iss` が**完全一致**すること
3. `aud` に自分のclient\_idが含まれること。信頼していない宛先が混ざっていたら拒否する
4. `azp` がある場合は、自分のclient\_idと一致するかを確かめる
5. OPが公開する鍵で署名を検証する。`alg` の既定はRS256
6. 現在時刻が `exp` より前であること
7. 必要なら `iat` で古すぎるトークンを拒否する（許容幅はRPが決める）
8. 認証リクエストで `nonce` を送ったなら、同じ値が入っていること
9. `acr` や `auth_time` を要求したなら、その値が要件を満たすこと

Coreは、トークンエンドポイントからTLSで直接受け取ったIDトークンに限り、署名検証の代わりにTLSのサーバー検証で発行者を確かめてもよいとしています。ただしこれは「省略してよい」という許可であって、ライブラリで署名を検証するほうが後からフローを変えたときにも安全です。

### audの検証でよくある間違い

`aud` は1つなら文字列、複数なら配列で入ります。文字列を前提に `payload.aud === clientId` と書くと、配列で届いたトークンをすべて拒否します。逆に「どれかが一致すればよい」とだけ書くと、Coreが求める「信頼していない宛先が混ざっていたら拒否」を満たしません。

もう1つの典型は、比較する値の取り違えです。IDトークンの `aud` と比べるのはOPに登録したclient\_idで、APIの識別子やアプリのURLではありません。複数のOPに対応するアプリでは、`iss` ごとにclient\_idが違うため、発行者とclient\_idの組で照合します。

### Node.jsとjoseによる検証コード

次のコードは、認可コードフローでトークンエンドポイントから受け取った署名付き（暗号化なし）のIDトークンを、Discoveryで取得した鍵と発行者を使って検証します。署名・`iss`・`aud`・`exp` と必須クレームの有無はjoseの `jwtVerify` がまとめて確かめ、Coreが追加で求める `sub` の形式・宛先の混在・`azp`・`nonce` を後段で見ています。`acr` や `auth_time` を要求した場合の確認は含めていません。`nonce` は呼び出し側で認証リクエストごとに生成してセッションに保存し、検証が済んだら削除して、同じ値とトークンの再利用を防ぎます。

```
// verify-id-token.mjs（Node.js 20以上・jose 6系）
import { createRemoteJWKSet, jwtVerify } from 'jose';

const ISSUER = process.env.OIDC_ISSUER;        // 例: https://accounts.google.com
const CLIENT_ID = process.env.OIDC_CLIENT_ID;  // OPに登録したclient_id
const TRUSTED_AUDIENCES = [CLIENT_ID];         // audに入っていてよい値

// 1. Discoveryでjwks_uriとissuerを取る（URLを手で書かない）
//    URLを組み立てるときだけ終端の / を除く（issとの比較にはISSUERをそのまま使う）
const conf = await (await fetch(`${ISSUER.replace(/\/$/, '')}/.well-known/openid-configuration`)).json();
if (conf.issuer !== ISSUER) throw new Error('issuer mismatch in discovery');
const JWKS = createRemoteJWKSet(new URL(conf.jwks_uri));

export async function verifyIdToken(idToken, expectedNonce) {
  // 2. 署名・iss・aud・exp をまとめて検証（algは登録時の既定RS256に固定）
  const { payload } = await jwtVerify(idToken, JWKS, {
    issuer: ISSUER,
    audience: CLIENT_ID,
    algorithms: ['RS256'],
    requiredClaims: ['iss', 'sub', 'aud', 'exp', 'iat'],  // 指定しないと欠落を通す
    clockTolerance: 60,                                    // 時計のずれを60秒まで許容
  });
  if (typeof payload.sub !== 'string' || !/^[\x20-\x7e]{1,255}$/.test(payload.sub)) {
    throw new Error('invalid sub');
  }
  // 3. audに信頼していない値が混ざっていたら拒否。azpがあれば自分のclient_idか
  if ([].concat(payload.aud).some((a) => !TRUSTED_AUDIENCES.includes(a))) {
    throw new Error('untrusted audience');
  }
  if (payload.azp !== undefined && payload.azp !== CLIENT_ID) throw new Error('azp mismatch');
  // 4. 認証リクエストで送ったnonceと一致するか
  if (payload.nonce !== expectedNonce) throw new Error('nonce mismatch');
  return { sub: payload.sub, iss: payload.iss };  // ユーザーの識別は iss+sub の組で行う
}
```

このコードに不正なトークンを渡すと、次のように止まります（jose 6.2.12・Node.js 26.5.0で実行）。

| トークンの状態           | 結果                                           |
| ----------------- | -------------------------------------------- |
| aud が別のclient\_id | ERR\_JWT\_CLAIM\_VALIDATION\_FAILED（aud）     |
| iss が別の発行者        | ERR\_JWT\_CLAIM\_VALIDATION\_FAILED（iss）     |
| aud に信頼外の値が混在     | untrusted audience                           |
| azp が別のclient\_id | azp mismatch                                 |
| nonce が不一致        | nonce mismatch                               |
| exp を60秒超過        | ERR\_JWT\_EXPIRED                            |
| exp・iat・sub が欠落   | ERR\_JWT\_CLAIM\_VALIDATION\_FAILED（missing） |

`algorithms` を指定しないと、トークン側のヘッダーが示すアルゴリズムをそのまま受け入れる余地が残ります。ここではCoreの既定であるRS256に固定しました。クライアント登録時に別のアルゴリズムを取り決めた場合は、OPの対応一覧ではなく登録した値に合わせます。`requiredClaims` を省くと、joseは `exp` などが欠けたトークンも通すので、必須の5項目は明示します。`createRemoteJWKSet` は未知の `kid` が来たときに鍵セットを取り直すため、OP側の鍵ローテーションにも追従します。

## 認証フローの種類とresponse\_typeの対応

OIDCのフローは、認可リクエストの `response_type` の値で決まります（Core 3章）。

| response\_type       | フロー        | アクセストークンの受け取り口 |
| -------------------- | ---------- | -------------- |
| code                 | 認可コードフロー   | トークンエンドポイント    |
| id\_token            | インプリシットフロー | 発行しない          |
| id\_token token      | インプリシットフロー | 認可レスポンス        |
| code id\_token       | ハイブリッドフロー  | トークンエンドポイント    |
| code token           | ハイブリッドフロー  | 認可レスポンス        |
| code id\_token token | ハイブリッドフロー  | 認可レスポンス        |

OAuth 2.0の `response_type=token` はIDトークンを返さないため、OIDCでは使いません（Core 3章の注記）。

### 認可コードフロー＋PKCEが既定になった理由

2025年1月に出たOAuth 2.0のセキュリティ指針[RFC 9700](https://www.rfc-editor.org/rfc/rfc9700.html)は、認可レスポンスでアクセストークンを返す型（表の「認可レスポンス」の3行）は漏えいと再利用に弱いとして、トークン注入と漏えい経路への対策が取れている場合を除き使わないよう求めています（2.1.2のSHOULD NOT）。代わりに挙げているのが `code` と、アクセストークンをトークンエンドポイントで返す `code id_token` です。さらに公開クライアント（SPAやスマホアプリ）ではPKCEが必須、機密クライアントでも推奨とされました（2.1.1）。

認可リクエストは次のような形になります。Core 3.1.2.1の例に、`nonce` とPKCEのパラメーターを足したものです（読みやすさのため改行しています）。

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

RFC 9700は、OIDCの `nonce` がCSRF対策にもなると述べています。ただしPKCEと `nonce` のどちらも、取引ごとに新しい値を作り、そのブラウザのセッションに結び付けて保存しなければ意味がありません。トークン交換までの具体的な実装は[認可コードフローとは？認可リクエストからトークン交換までの実装手順と検証設計を解説](/tech/details/16415/)、`code_verifier` の作り方は[PKCEとは？認可コード横取り攻撃を防ぐcode\_verifierの実装と検証設計を解説](/tech/details/16405/)にまとめています。

### インプリシットフロー・ハイブリッドフローが残る場面

`response_type=id_token` のインプリシットフローはアクセストークンを発行しないため、RFC 9700が非推奨とする「認可レスポンスでアクセストークンを返す型」には当たりません。それでもIDトークンがURLフラグメントでブラウザに渡るので、新規に採用する理由はほとんどありません。ハイブリッドフローの `code id_token` は、認可レスポンスの段階でIDトークンを受け取り、`c_hash` で認可コードの差し替えを検知したい場合に使われます。金融APIのように要件が厳しい領域以外では、`code` だけで足ります。

## スコープとUserInfoエンドポイントで取れる情報

`openid` 以外に、Core 5.4は4つのスコープを定めています。

| スコープ    | 返るクレーム                                              |
| ------- | --------------------------------------------------- |
| profile | name・given\_name・family\_name・picture・locale など14項目 |
| email   | email・email\_verified                               |
| address | address                                             |
| phone   | phone\_number・phone\_number\_verified               |

アクセストークンを発行するフローでは、これらの属性はアクセストークンを使って**UserInfoエンドポイント**から取得するのが基本です（Core 5.4）。UserInfoの応答に入る `sub` は、IDトークンの `sub` と完全一致することを確かめてから使います（Core 5.3.2）。一致を見ないと、別ユーザーの属性を取り違えるおそれがあります。IDトークンにも属性を入れるかどうかはOP次第で、Googleは `claims_supported` に `email` や `name` を挙げています。どのスコープに対応しているかは、次に説明するDiscoveryの `scopes_supported` で確認できます。Googleは `openid`・`email`・`profile` の3つだけを挙げています（2026年9月27日取得）。

## Discoveryとopenid-configurationで分かること

[OIDC Discovery 1.0](https://openid.net/specs/openid-connect-discovery-1%5F0.html)に対応したOPは、発行者URLの末尾に `/.well-known/openid-configuration` を付けた場所で設定をJSONで公開します。発行者URLが `/` で終わる場合は、その `/` を除いてから付け足します。RPはここから各エンドポイントのURLと公開鍵の場所を取得できるので、URLをコードに書き込む必要がありません。Googleの設定は次のコマンドで取れます。

```
curl -s https://accounts.google.com/.well-known/openid-configuration
```

2026年9月27日に取得した結果のうち、主な項目は次のとおりです。

```
{
  "issuer": "https://accounts.google.com",
  "authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth",
  "token_endpoint": "https://oauth2.googleapis.com/token",
  "userinfo_endpoint": "https://openidconnect.googleapis.com/v1/userinfo",
  "jwks_uri": "https://www.googleapis.com/oauth2/v3/certs",
  "subject_types_supported": ["public"],
  "id_token_signing_alg_values_supported": ["RS256"],
  "code_challenge_methods_supported": ["plain", "S256"],
  "authorization_response_iss_parameter_supported": true
}
```

Discovery 1.0で必須の項目は、`issuer`・`authorization_endpoint`・`jwks_uri`・`response_types_supported`・`subject_types_supported`・`id_token_signing_alg_values_supported` の6つです。`token_endpoint` はインプリシットフローだけのOPでなければ必須、`userinfo_endpoint` は推奨です。`issuer` の値は、IDトークンの `iss` と完全一致で比べる相手になります。Googleの例では末尾にスラッシュが付いていないので、`https://accounts.google.com/` と書いた設定では一致しません。

## OIDCとSAMLの違いと選び方

| 観点       | OIDC                     | SAML 2.0           |
| -------- | ------------------------ | ------------------ |
| 策定       | OpenID Foundation（2014年） | OASIS（2005年）       |
| 本人確認の形式  | IDトークン（JWT）              | アサーション（XML）        |
| API認可    | OAuth 2.0と一体             | 別途OAuth等が必要        |
| 主なログイン経路 | ブラウザ・ネイティブアプリ            | ブラウザ（ECPプロファイルもある） |
| 主な利用先    | ソーシャルログイン・SaaS・API       | 社内SSO・既存の業務SaaS    |

新しく作るアプリは、OIDCを第一候補にします。1回の認可コードフローでIDトークンとAPI用のアクセストークンを同時に受け取れること、スマホアプリ向けにPKCE付きの認可コードフローがRFC 8252として標準化されていることが理由です。SAMLを選ぶのは、つなぎ先の社内IdPや業務SaaSがSAMLしか受け付けない場合です。方式を決める前に、つなぐ予定のIdPと業務SaaSそれぞれの管理画面やドキュメントで、OIDCのアプリ登録とSAMLのどちらに対応しているかを確かめます。SAML側のフローと署名検証の勘所は[SAMLとは？認証フロー・IdP/SPの仕組みとOAuth・OIDCとの使い分けを実装視点で解説](/tech/details/13299/)で扱っています。

## GitHub ActionsのOIDC連携：人がいない認証への応用

OIDCはログイン画面のない場面でも使われています。代表例がGitHub Actionsからクラウドへの認証です。ワークフローに `id-token: write` の権限を与えると、GitHubのOP（`https://token.actions.githubusercontent.com`）がジョブごとに短命なIDトークンを発行します。AWS・Google Cloud・Azureなどはこのトークンを検証し、条件に合えば一時的な認証情報を返します。クラウドのアクセスキーをSecretsに保存しなくて済むのが利点です。

```
permissions:
  id-token: write   # OIDCトークンの取得に必要
  contents: read

steps:
  - uses: aws-actions/configure-aws-credentials@v6
    with:
      role-to-assume: arn:aws:iam::123456789012:role/deploy
      aws-region: ap-northeast-1
      # audience の既定値は sts.amazonaws.com
```

クラウド側の信頼条件で見るのは `aud` と `sub` です。GitHubのIDトークンの `aud` は、既定ではリポジトリ所有者のURL（`https://github.com/my-org` など）で、AWSのアクションは `sts.amazonaws.com` を指定してトークンを取得します。`sub` は `repo:octo-org/octo-repo:ref:refs/heads/main` のようにリポジトリとブランチ・環境を連結した値です。

2026年7月15日以降に作成されたリポジトリでは、`sub` の既定形式が `repo:octo-org@123456/octo-repo@456789:ref:refs/heads/main` のように所有者IDとリポジトリIDを含む形へ変わりました。組織名を手放した後に別人が同じ名前を取ると、旧形式では同じ `sub` が作れてしまうためです。それより前に作成されたリポジトリは、組織・リポジトリ単位でオプトインするか、2026年7月15日以降に改名・移管されない限り旧形式のままです。GitHub Enterprise Serverはこの変更の対象外です（[GitHub DocsのOIDCリファレンス](https://docs.github.com/en/actions/reference/security/oidc)）。信頼ポリシーを書き写すときは、どちらの形式のリポジトリかを先に確認します。Secretsからの移行手順は[GitHub ActionsのSecrets管理｜置き場所の選定と漏えい経路・OIDC移行の判断](/tech/details/16939/)にまとめています。

## OIDC導入で避けたい設計

### メールアドレスをユーザーのキーにする設計

Core 5.7は、RPが安定した識別子として頼れるのは `iss` と `sub` の組だけだと定めています。発行者は同じ `email` を別のユーザーに再利用してよいとされており、退職者のアドレスが新入社員に割り当てられると、メールアドレスで照合しているアプリは前任者のアカウントに新入社員を入れてしまいます。複数のOPを受け入れるなら、`sub` だけでも不十分で、`iss` と組にして保存します。`email_verified` が `true` でも、そのアドレスの過去の持ち主と今の持ち主が同じ人だとは保証されません。メールアドレスが一致する既存アカウントとひも付けるときは、自動では行わず、既存アカウント側のログインもさせて両方の本人確認が済んでから連携します。

### OPを自作する判断

RPを作るのとOPを作るのとでは、難しさが桁違いです。OPは鍵のローテーション、同意画面、セッション管理、ログアウト連携、Discoveryの提供まで背負います。社内の認証基盤が必要なら、KeycloakやOry Hydraのような実績のあるソフトウェアか、IDaaSを使うのが現実的です。Keycloakの立ち上げ方は[Keycloakの使い方｜起動から管理コンソール・DB設定と権限モデルまで](/tech/details/3293/)で扱っています。同じ理由で、RP側もIDトークンの検証を自前のJWT解析で書かず、OpenID Foundationの認定を受けたライブラリか、上のjoseのような署名検証済みのライブラリを使います。

サーバー同士の通信（夜間バッチが別システムのAPIを呼ぶなど）は、ユーザーのログインとは別に、ワークロードの認証として設計します。呼び出し先が自社APIでOAuth 2.0のアクセストークンを受け付けるなら、クライアントクレデンシャルフローで足ります。一方、GitHub Actionsのクラウド連携やCloud Runのサービス間呼び出しのように、呼び出し先がOIDCのIDトークンを要求する構成もあるので、方式は呼び出し先の受け付ける形式に合わせます。

## よくある質問

### OIDCとOAuth 2.0の違いは何ですか？

OAuth 2.0はAPIを呼ぶ権限を委ねる「認可」の仕様で、OIDCはその上で「ログインしたのは誰か」を確かめる「認証」の仕様です。OIDCはOAuth 2.0の認可コードフローなどをそのまま使い、`scope=openid` を指定したときにIDトークンを追加で返します。

### OIDCの読み方は何ですか？

OpenID Connectは「オープンアイディー・コネクト」と読みます。略称のOIDCは、アルファベットのまま「オーアイディーシー」と読むのが一般的です。

### IDトークンとアクセストークンはどう使い分けますか？

ログインで受け取ったIDトークンはアプリ自身が読んでログインを成立させるためのもので、APIには送りません。アクセストークンはAPIを呼ぶときに `Authorization: Bearer` ヘッダーで送ります。ログイン用IDトークンの `aud` はアプリのclient\_idなので、宛先を検証するAPIに送れば拒否されます。

### IDトークンのaudとは何ですか？

そのIDトークンを受け取るべきアプリ（RP）のclient\_idです。受け取ったアプリは自分のclient\_idが含まれ、信頼していない値が混ざっていないことを検証します。省くと、別のアプリ向けに発行されたトークンでログインできてしまいます。

### OIDCに対応しているサービスには何がありますか？

Google・Microsoft Entra ID・Apple・Auth0・Okta・Amazon Cognitoなどがあり、自社で運用するならKeycloakやOry Hydraが使えます。対応状況は各サービスの `/.well-known/openid-configuration` を取得すれば確認できます。

## 関連記事

- [OAuth 2.0とは？仕組み・認可フローと認証との違い、OAuth 2.1の変更点をわかりやすく解説](/tech/details/8369/)
- [認可コードフローとは？認可リクエストからトークン交換までの実装手順と検証設計を解説](/tech/details/16415/)
- [JWTとは？構造・署名検証の仕組みとセッション・OAuth/OIDCとの違いを実装視点で解説](/tech/details/13453/)
- [SAMLとは？認証フロー・IdP/SPの仕組みとOAuth・OIDCとの使い分けを実装視点で解説](/tech/details/13299/)
- [シングルサインオン（SSO）とは？4つの実現方式とIDトークン検証・IdP選定を実装手順で解説](/tech/details/15821/)

---

出典: [OIDC（OpenID Connect）とは？IDトークンの検証手順とOAuth 2.0との違いを解説](<https://www.issoh.co.jp/tech/details/6532/>)（株式会社一創）
