多くの企業が OAuth 2.0 と OpenID Connect(OIDC)を混同しています。OAuth 2.0 は認可を、OpenID Connect は認証を行うプロトコルですが、実装時にはその違いを明確に理解することが重要です。本記事では、OIDC と OAuth 2.0 の根本的な違い、Authorization Code Flow の実装フロー、IDトークン・アクセストークンの役割、OIDC を支える技術仕様、SAML 認証との対比を含むセキュアな実装パターンを詳細に解説します。
OAuth 2.0 と OpenID Connect:本質的な違い
OAuth 2.0:認可を行うプロトコル
OAuth 2.0 は、ユーザーがアプリケーションに対して、自分のリソースへのアクセス権を委譲するプロトコルです。
具体例:
- Spotify アプリが Facebook のあなたの友達情報にアクセスする権限をもらう
- Slack が Google Drive のドキュメント一覧を読み取る権限をもらう
OAuth 2.0 の本質:
ユーザー → アプリケーション:「Google の友達リストにアクセスしてもいい?」
↓
ユーザー → Google:「アプリに許可を与える」(同意画面)
↓
Google → アプリケーション:「アクセストークン」を発行
↓
アプリケーション → Google:「アクセストークン」を使用して友達リストを取得
重要なポイント:
- OAuth 2.0 はユーザーの**認証(Who you are)**を行わない
- リソースサーバーへのアクセス権(スコープ)を管理するだけ -「このユーザーは誰か」という情報は含まれていない
実装では、アクセストークンが発行されても、それがどのユーザーのものか、アプリケーションからは直接わかりません。
OpenID Connect:認証を行うプロトコル
OpenID Connect(OIDC)は OAuth 2.0 の上に構築された認証レイヤーです。OIDC は、ユーザーが「誰であるか」を確認する情報を提供します。
具体例:
- Slack にサインアップするとき、Google アカウントでログインする
- Notion で Microsoft 365 アカウントを使用してログインする
OIDC の本質:
ユーザー → アプリケーション:「Google でログインしたい」
↓
ユーザー → Google:「ログイン認証」(パスワード・MFA)
↓
Google → アプリケーション:「IDトークン」+「アクセストークン」を発行
↓
アプリケーション → Google:「IDトークン」を検証し、ユーザー情報を確認
OIDC が OAuth 2.0 に追加するもの:
-
IDトークン(JWT形式)
- ユーザーの身元情報(ユーザーID、メールアドレス、表示名など)を含む
- デジタル署名されており、改ざん検知が可能
-
UserInfo エンドポイント
- アクセストークンを使用して、ユーザーの詳細情報を取得
-
認証フロー標準化
- Authorization Code Flow、Implicit Flow、Hybrid Flow など複数の認証フローを定義
IDaaS導入RFPテンプレート
IDaaS導入の社内稟議に使えるRFPテンプレート。SSO、MFA、プロビジョニングなどの要件を網羅したフォーマットです。
Authorization Code Flow:最も安全なフロー
OIDC/OAuth 2.0 で最も一般的で安全な実装パターンは Authorization Code Flow です。
フロー図:7ステップの認証プロセス
┌─────────────────┐ ┌──────────────────┐
│ │ │ │
│ ユーザー │ │ ブラウザ │
│ (リソース │ │ (User Agent) │
│ オーナー) │ │ │
└────────┬────────┘ └────────┬─────────┘
│ │
│ 1. ログインクリック │
│───────────────────────────────────────→
│ │
│ ┌──────────────────┐
│ │ アプリケーション │
│ │ (Client) │
│ │ │
│ │ 2. Authorization
│ │ リクエスト
│ 3. Google/Microsoft │ を構成 │
│ ログイン画面表示 │ │
│←─────────────────────────────────│
│ │
│ 4. ユーザー認証 │
│ (ID + パスワード) │
│───────────────────────────────────→
│ │
│ 5. 同意画面表示 │
│←─────────────────────────────────│
│ │
│ 6. 「同意する」クリック │
│───────────────────────────────────→
│ │
│ ┌──────────────────┐
│ │ IdP │
│ │ (Google/ │
│ │ Microsoft) │
│ │ │
│ 7. Authorization Code │ │
│ リダイレクト │ │
│←─────────────────────────────────│
│ │
│ ↓↓↓ バックチャネル通信 ↓↓↓
│
│ ┌──────────────────┐
│ │ アプリケーション │
│ 8. IDトークン │ バックエンド │
│ + アクセストークン │ サーバー │
│←──────────────────────────│ │
│ │ Authorization │
│ │ Code を使って │
│ │ トークン要求 │
│ │─────────────→ │
│ │ │
│ └──────────────────┘
│
ログイン完了
実装の具体的なステップ
ステップ 1-2:ユーザーがログインをリクエスト
// ブラウザ側の処理
const authorizationUrl = new URL(
'https://accounts.google.com/o/oauth2/v2/auth'
);
authorizationUrl.searchParams.append('client_id', 'xxxxx.apps.googleusercontent.com');
authorizationUrl.searchParams.append('response_type', 'code');
authorizationUrl.searchParams.append('scope', 'openid email profile');
authorizationUrl.searchParams.append('redirect_uri', 'https://myapp.com/callback');
authorizationUrl.searchParams.append('state', generateRandomState()); // CSRF 対策
window.location.href = authorizationUrl.toString();
ステップ 3-6:Identity Provider(IdP)でユーザー認証
IdP(Google、Microsoft など)がユーザーの身元を確認します。
- 認証: パスワード、MFA(SMS/TOTP/セキュリティキー)
- 同意: ユーザーがアプリケーションへの情報提供に同意
ステップ 7:Authorization Code をブラウザにリターン
ブラウザが以下の URL にリダイレクト:
https://myapp.com/callback?code=4/0AX4...(長いコード)&state=random123
ステップ 8:バックエンド サーバーが Authorization Code をトークンと交換
このステップはバックチャネル通信(ブラウザを経由しない直接通信)で実行されます。
# バックエンド サーバーから Google へ直接 POST リクエスト
curl -X POST https://oauth2.googleapis.com/token \
-d "client_id=xxxxx.apps.googleusercontent.com" \
-d "client_secret=yyyyy" \
-d "code=4/0AX4..." \
-d "redirect_uri=https://myapp.com/callback" \
-d "grant_type=authorization_code"
# レスポンス:
{
"access_token": "ya29.xxx",
"expires_in": 3599,
"refresh_token": "1//xxx",
"scope": "openid email profile",
"token_type": "Bearer",
"id_token": "eyJhbGci..." ← これが OIDC のキー
}
IDトークン と アクセストークン:役割の違い
OIDC/OAuth 2.0 では、2 種類のトークンが発行されます。その役割は全く異なります。
IDトークン(ID Token):ユーザー身元を証明
形式: JWT(JSON Web Token)
IDトークンのペイロード例:
{
"iss": "https://accounts.google.com",
"azp": "xxxxx.apps.googleusercontent.com",
"aud": "xxxxx.apps.googleusercontent.com",
"sub": "110169547860073334883", ← ユーザーの一意 ID
"email": "user@example.com",
"email_verified": true,
"at_hash": "HK3E...",
"iat": 1312883113,
"exp": 1312886713,
"name": "山田太郎",
"picture": "https://lh3.googleusercontent.com/...",
"locale": "ja"
}
IDトークンの特徴:
- JWT 形式で、署名によって改ざん検知が可能
- ユーザーの身元情報(name、email、picture など)を含む
- ブラウザでも検証可能(署名鍵は IdP が公開)
- 通常は短命(1 時間程度)
IDトークンの検証ロジック(重要):
# バックエンド サーバーでの検証
import jwt
def verify_id_token(id_token, client_id):
try:
# 公開鍵を取得(IdP から)
public_keys = get_public_keys_from_idp()
# JWT を検証
decoded = jwt.decode(
id_token,
public_keys,
algorithms=['RS256'],
audience=client_id,
issuer='https://accounts.google.com'
)
# iss, aud, exp などを検証
assert decoded['iss'] == 'https://accounts.google.com'
assert decoded['aud'] == client_id
assert decoded['exp'] > time.time()
return decoded # ユーザー情報
except jwt.InvalidSignatureError:
raise Exception("署名が無効です(改ざんの可能性)")
except jwt.ExpiredSignatureError:
raise Exception("トークンが期限切れです")
アクセストークン(Access Token):リソースアクセス権を許可
形式: Bearer token(不透明な文字列、通常は JWT)
アクセストークンの役割:
- リソースサーバーへのアクセス権を表現
- UserInfo エンドポイントへのアクセスに使用
- 他の API(Google Drive API など)へのアクセスに使用
アクセストークンの使用例:
# Google UserInfo エンドポイントにアクセス
curl -H "Authorization: Bearer ya29.xxx" \
https://www.googleapis.com/oauth2/v2/userinfo
# レスポンス(IDトークンとは異なるデータ取得可能性):
{
"id": "110169547860073334883",
"email": "user@example.com",
"verified_email": true,
"name": "山田太郎",
"picture": "https://lh3.googleusercontent.com/...",
"locale": "ja"
}
重要な違いまとめ
| 項目 | IDトークン | アクセストークン |
|---|---|---|
| 目的 | ユーザー認証 | リソースアクセス権 |
| 形式 | JWT | Bearer token |
| 内容 | ユーザー身元情報 | アクセス権限 |
| 有効期限 | 短い(1時間程度) | 中程度(1日~数週間) |
| 署名検証 | 必須 | 不要(IdP が信頼) |
| 漏洩時の影響 | ユーザーになりすまし可能 | リソースアクセスのみ可能 |
| 送信方法 | ブラウザ・API 両方 | API のみ |
セキュアな実装パターン
パターン 1:SPA(Single Page Application)+ バックエンド
最も推奨される実装パターンです。
アーキテクチャ:
ブラウザ (SPA) ← Auth Cookie
↓
バックエンド サーバー ← Secure HttpOnly Cookie (IDトークン)
↓
Google / Microsoft / LOCKED MSO
実装の流れ:
// フロントエンド(SPA)
const response = await fetch('https://myapp.com/api/auth/login', {
method: 'POST',
credentials: 'include', // クッキーを自動送信
body: JSON.stringify({ provider: 'google' })
});
// バックエンドが Authorization URL を返す
const { authorizationUrl } = await response.json();
window.location.href = authorizationUrl;
// ↓ IdP で認証後、以下にリダイレクト
// バックエンド(Secure HttpOnly Cookie を設定)
app.get('/callback', (req, res) => {
const code = req.query.code;
// トークン交換
const tokens = await exchangeCodeForTokens(code);
// IDトークンを検証
const userInfo = verifyIdToken(tokens.id_token);
// Secure HttpOnly Cookie を設定
res.cookie('session_id', generateSessionId(), {
httpOnly: true,
secure: true,
sameSite: 'Strict'
});
res.redirect('https://myapp.com/dashboard');
});
このパターンの利点:
- IDトークン・アクセストークンはブラウザに保存されない(XSS 攻撃対策)
- バックエンド サーバーでトークン検証を実施
- Secure HttpOnly Cookie を使用(CSRF 攻撃対策)
パターン 2:ネイティブアプリ(iOS/Android)
ネイティブアプリでは、デバイス内の安全なストレージを活用します。
// iOS での OIDC 実装例(ASWebAuthenticationSession 使用)
import AuthenticationServices
func initiateLogin() {
let authorizationUrl = URL(string: "https://idp.com/oauth/authorize?client_id=...")!
let session = ASWebAuthenticationSession(
url: authorizationUrl,
callbackURLScheme: "myapp"
) { callbackURL, error in
guard let code = callbackURL?.queryItems?.first(where: { $0.name == "code" }) else {
return
}
// トークン交換
exchangeCodeForTokens(code.value) { tokens in
// トークンは Keychain に安全に保存
KeychainStorage.save(tokens.id_token, forKey: "id_token")
KeychainStorage.save(tokens.access_token, forKey: "access_token")
}
}
session.presentationContextProvider = self
session.start()
}
パターン 3:BFF パターン(Backends for Frontends)
複数のフロントエンド(Web、iOS、Android)がある場合、BFF パターンが効果的です。
Web SPA ←→ Web BFF ←→ Google / Microsoft / LOCKED MSO
iOS App ←→ iOS BFF ←→
Android App ←→ Android BFF ←→
各 BFF がプラットフォーム固有の認証フローを実装し、バックエンド API はすべての BFF から認証されたリクエストを受け取ります。
LOCKED MSO での OpenID Connect 実装
標準搭載機能
LOCKED MSO は OIDC(OpenID Connect)対応の IdP として動作します。
主要機能:
- 完全な OIDC 2.0 準拠: Authorization Code Flow、Implicit Flow、Hybrid Flow に対応
- マルチプロトコル対応: SAML 2.0、OAuth 2.0 と同時対応
- UserInfo エンドポイント: アクセストークンを使用した詳細ユーザー情報取得
- アクセス制御: 12 項目の条件組み合わせによる動的フロー
- 日本語対応: ドキュメント・サポート完全日本語対応
実装手順
ステップ 1:アプリケーションを登録
LOCKED MSO 管理画面:
セッティング → アプリケーション → 新規登録
アプリケーション名:MyWebApp
プロトコル:OpenID Connect
リダイレクト URI:https://myapp.com/callback
ステップ 2:Authorization URL を構成
https://locked.jp/oauth2/authorize?
client_id=xxxxx
&response_type=code
&scope=openid email profile
&redirect_uri=https://myapp.com/callback
&state=random123
ステップ 3:IDトークン検証設定
oidc_config:
verify_signature: true
verify_expiration: true
verify_issuer: true
issuer: "https://locked.jp"
allowed_algorithms: ["RS256"]
ステップ 4:テスト
# 実装したアプリケーションをテスト
curl -X POST https://locked.jp/oauth2/token \
-d "grant_type=authorization_code" \
-d "code=xxxx" \
-d "client_id=yyyy" \
-d "client_secret=zzzz" \
-d "redirect_uri=https://myapp.com/callback"
# レスポンス
{
"id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"access_token": "ya29.c.xxx",
"token_type": "Bearer",
"expires_in": 3600
}
実装企業の事例
事例 1:SaaS スタートアップ(ユーザー数 10,000 名)
導入前:
- カスタム認証実装による開発リソース消費
- パスワード再設定の対応で月 50 時間
LOCKED MSO + OIDC 導入:
- Authorization Code Flow で安全に実装
- SPA + バックエンド パターンを採用
導入結果(3 ヶ月後):
- 開発時間削減:月 50 時間 → 0 時間
- ユーザー登録フロー簡素化:5 ステップ → 1 ステップ
- パスワードリセット対応削減:月 50 時間 → 月 5 時間
事例 2:エンタープライズ SaaS(従業員 500 名)
導入前:
- Azure AD / Google Workspace / HENNGE One との連携が複雑
- Protocol により対応状況が異なる
LOCKED MSO + マルチプロトコル対応:
- OIDC(Google Workspace)
- SAML 2.0(Azure AD)
- を同時対応
導入結果(4 ヶ月後):
- 統一認証基盤の確立
- ユーザー管理の一元化
- インシデント対応の簡素化(年間 200 時間削減)
まとめ:OIDC で安全で使いやすい認証を実現
OpenID Connect は OAuth 2.0 の上に認証レイヤーを追加することで、セキュアで標準化された認証をもたらします。Authorization Code Flow を正しく実装することで、XSS・CSRF・トークン盗難などの攻撃に強い認証基盤が実現できます。
LOCKED MSO を活用することで、複雑な OIDC 実装を避け、数日で本番環境への展開が可能です。
外部参考リンク
関連記事
- SAML 2.0仕様書日本語詳解【RFC 7114準拠実装】
- SSOセキュリティ課題完全対策【トークンハイジャック・セッション固定攻撃防止】
- SAML vs OAuth完全比較【プロトコル仕様から実装まで】
- MFA運用完全ガイド【TOTP/FIDO2/SMS比較と段階導入】
- Google Workspace×SSO設定完全ガイド【実装6ステップ】
LOCKED MSO で OIDC 実装を簡素化しませんか?
セキュアな Authorization Code Flow をわずか数日で展開できます。複数の IdP 連携も簡単に実現可能です。