logoChibiham
cover
🔑

認証認可アーキテクチャの変遷(1)モノリスから認証基盤へ

本シリーズは5部構成である。

なぜ「変遷」として追うのか

OAuth 2.0やOIDCの解説記事は世の中に無数にある。しかし多くは「フローの説明」に終始していて、なぜそのアーキテクチャが必要になったのか、つまり解決しようとした課題の文脈が抜け落ちていると感じている。

認証認可のアーキテクチャは、システム構成(モノリス→マイクロサービス)と組織構成(単一チーム→複数チーム)の変化に引きずられて進化してきた。逆に言えば、自分の組織がどの段階にいるかを見誤ると、過剰な認証基盤を作って運用コストに沈むか、脆弱な認証を放置してインシデントを起こすかのどちらかになる。

このシリーズでは、アーキテクチャの各段階を「解決する課題」「導入難易度」「組織的前提」とセットで整理する。法律のアナロジーで言えば、条文(仕様)だけでなく立法事実(なぜその法が必要だったか)を追う読み方だ。仕様は立法事実を知らないと正しく適用できない。

Stage 0: モノリス + セッションCookie

出発点を確認しておく。伝統的なWebアプリケーションの認証はこうだった。

  • アプリ自身がユーザーDBを持ち、パスワードを検証する
  • 認証状態はサーバー側セッション(メモリ/Redis/DB)に保持し、CookieのセッションIDで参照する
  • 認可は「セッションからユーザーを引き、そのユーザーのロールをアプリ内のコードで判定する」

これは悪いアーキテクチャではない。むしろ単一アプリ・単一チームの世界では最適に近いと考えている。理由は:

  • セッションは即時失効できる(ログアウト=サーバー側で破棄すれば終わり)
  • Cookieは HttpOnly / Secure / SameSite でブラウザが守ってくれる
  • 認証と認可が同一プロセス内にあるので、整合性の問題が起きない

後の段階で登場するJWTやトークンは、この「即時失効」「ブラウザによる保護」を一部手放す代わりに別の課題を解決する。トレードオフなしに進化した段階は一つもない、というのがこのシリーズの通奏低音である。

Stage 0が破綻する条件

このアーキテクチャに限界が来るのは、次のような要求が発生したときだ。

発生する要求なぜセッションCookieでは苦しいか
サービスが2つ以上になる(社内SSO)Cookieはオリジン単位。ドメインが異なるサービス間で認証状態を共有できない
モバイルアプリ対応Cookieジャーの挙動がWebと異なり、セッション管理が不安定。またアプリにID/パスワードを長期保持させたくない
外部パートナーへのAPI公開「パスワードを預かってスクレイピング」しか手段がなくなる(後述のパスワードアンチパターン)
ユーザーDBの重複サービスごとにユーザーテーブルとパスワードハッシュが増殖し、漏洩リスクと運用負荷が線形に増える
MFA・パスワードレス対応各アプリが個別にWebAuthnFIDO2を実装するのは非現実的

パスワードアンチパターン — OAuthの立法事実

OAuth誕生の直接のきっかけを一つ挙げるなら「パスワードアンチパターン」だ。2000年代、あるサービスが別サービスのデータにアクセスしたいとき、取られた手段はユーザーにそのサービスのID/パスワードを入力させ、預かったパスワードで代理ログインすることだった(Gmailの連絡先をSNSにインポートする、など)。

これの何が問題か:

  1. 全権委任しかできない。連絡先だけ読みたいのに、メール削除もできる権限を渡してしまう。最小権限の原則の完全な放棄である
  2. 失効できない。アクセスを止めるにはパスワード変更しかなく、他の連携も全部道連れになる
  3. サードパーティがパスワードを平文で保持する。漏洩したら本体アカウントごと乗っ取られる

OAuthはこれを「パスワードの代わりに、スコープが限定され、個別に失効できるトークンを渡す」ことで解決した。つまりOAuthの本質は認証プロトコルではなく、委任(delegation)のプロトコルである。この出自を押さえておくと、後述の「OAuthで認証してはいけない」問題が理解しやすくなる。

OAuth 2.0の登場人物と基本フロー

用語を整理する。OAuth 2.0(RFC 6749)は4つのロールを定義している。

ロール役割
Resource Ownerリソースの所有者=ユーザーあなた
Clientリソースにアクセスしたいアプリ連携アプリ、自社フロントエンド
Authorization Server (AS)トークンを発行するサーバーKeycloak, Auth0, Google
Resource Server (RS)リソースを提供するAPI自社API、Gmail API

中心となる**認可コードフロー(Authorization Code Flow)**はこうだ。

設計上のポイントは、経路がフロントチャネル(ブラウザのリダイレクトを経由する、傍受されやすい経路)とバックチャネル(サーバー間の直接通信、TLSで保護される経路)に分かれていることだ。危険なフロントチャネルには短命で使い捨ての「認可コード」しか流さず、本命のアクセストークンはバックチャネルでのみ受け渡す。この二段構えが認可コードフローの核心で、第3部で扱うPKCEDPoPは「それでもフロントチャネルは危ない」という前提でさらに防御を重ねる話になる。

詳細はOAuth2も参照。

「OAuthは認証ではない」問題とOIDC

OAuthのアクセストークンは「Clientがリソースにアクセスしてよい」ことしか意味しない。これを「ユーザーが誰であるか」の証明に流用すると脆弱性になる。有名なのがトークン置換攻撃だ:悪意あるアプリAがユーザーから正規に取得したアクセストークンを、別のアプリBの「OAuthログイン」に流し込むと、Bはトークンが有効なので本人だと誤認する。トークンには「誰に対して発行されたか(audience)」の検証手段が標準化されていなかったからだ。

OpenID Connect(OIDC)はこの穴を塞ぐためにOAuth 2.0の上に載せられた認証レイヤーである。追加されたものの中で本質的なのは:

  • IDトークン: 署名付きJWT。iss(発行者)、sub(ユーザー識別子)、aud(このClientのために発行された)、nonce(リプレイ防止)を含む。aud検証があるため他アプリへの流用ができない
  • UserInfoエンドポイントと標準クレーム(name, emailなど)
  • Discovery/.well-known/openid-configuration): エンドポイントや公開鍵の場所を機械可読にし、統合コストを下げた

audについて補足しておく。audは「このトークンの想定受取人」を表す汎用クレームで(RFC 7519)、IDトークンでは受取人=Clientなのでclient_idが入る(OIDC CoreでMUST)。一方、アクセストークンのaudに入るのはResource Serverの識別子であってclient_idではない(RFC 9068)——受取人がRSだからだ。またaudが複数の場合に「どのClientがこのトークンを取得したか」を示すazp(authorized party)という別クレームがある。「誰に向けたトークンか(aud)」と「誰が取得したトークンか(azp / client_id)」は別の概念であり、この区別が第2部のToken Exchange(audを宛先サービスに絞って交換する)の理解にも効いてくる。

実務上の整理としては「アクセストークンはAPIへの入場券、IDトークンはClientに提示する身分証明書」と覚えるのが良いと思う。IDトークンをAPIに送るのも、アクセストークンで認証するのも、どちらも誤用である。

トークン形式のトレードオフ: JWT vs 参照トークン

アクセストークンの中身は仕様上自由で、大きく2方式ある。ここは後のAPI Gateway設計(第2部)に直結するので先に押さえる。

自己完結型 (JWT)参照型 (opaque)
検証方法RSの手元で署名検証(公開鍵はJWKSから取得)ASのイントロスペクションエンドポイントに問い合わせ(RFC 7662)
レイテンシネットワーク往復なし。速い毎回AS往復(キャッシュで緩和)
即時失効できない。有効期限まで生き続けるできる。ASが失効を即座に反映
情報漏洩ペイロードはBase64で誰でも読める。クライアントに渡すトークンに内部情報を入れると漏れる中身は漏れない
ASへの負荷低い高い(全API呼び出しが集中)

JWTの「即時失効できない」は思った以上に効いてくる制約で、退職者のアクセス遮断や漏洩時の緊急対応で問題になる。実務では「アクセストークンを短命(5〜15分)にし、失効判断はリフレッシュトークン更新時にASで行う」という折衷が定石になっている。つまり失効の即時性を「最大15分の遅延」まで許容する設計判断であって、これを許容できないドメイン(金融の取引実行など)では参照型やハイブリッド(第2部のPhantom Tokenパターン)を選ぶことになる。

Stage 1: 認証基盤(IdP)の導入

ここからが最初のアーキテクチャ移行である。モノリス(あるいは数個のアプリ)から認証機能を切り出し、専用の認証基盤——OIDCの用語ではOpenID Provider、一般にはIdP(Identity Provider)——を立てる。

各アプリは自前のログイン画面とパスワードテーブルを捨て、IdPへのリダイレクトに置き換える。アプリはOIDCのRelying Party(RP)になる。

解決する課題

  1. SSO: IdPが自分のドメインでセッションCookieを持つため、アプリ1でログイン済みならアプリ2へのリダイレクトは一瞬で認可コードが返る。ユーザー体験として「一度のログインで全社サービスに入れる」が実現する
  2. クレデンシャルの集中管理: パスワードハッシュの保管場所が1箇所になる。漏洩監視・パスワードポリシー・アカウントロックの実装が1回で済む
  3. MFA/パスワードレスの一元化: WebAuthn対応をIdPに1回実装すれば全アプリに波及する。これは各アプリ実装では事実上不可能だった
  4. 監査の一元化: 「誰がいつどのアプリにログインしたか」がIdPのログに揃う。SOC2やISMS対応で効く
  5. 入退社処理(プロビジョニング)の一元化: 退職者のアカウント無効化が1箇所で完結する。人事システムとのSCIM連携もIdPだけ対応すればよい

新たに生まれる課題

導入すれば終わりではなく、新しい問題が生まれる。ここを甘く見た導入プロジェクトが炎上する。

  • セッションの二重管理: IdPのセッションと各アプリのセッションが別々に存在する。「IdPからログアウトしたのにアプリには入れたまま」問題が必ず起きる。OIDCにはFront-Channel / Back-Channel Logoutの仕様があるが、実装は各アプリに波及するため意外と重い
  • 既存ユーザーDBの移行: パスワードハッシュのアルゴリズムが合わずインポートできない、メールアドレス重複、複数アプリ間での同一人物の名寄せ——地味だがプロジェクト工数の大半はここに消える。私の感覚では、IdP導入の技術的難所は認証プロトコルではなくデータ移行と例外ユーザーの処理にある
  • 単一障害点化: IdPが落ちると全サービスにログインできなくなる。可用性要件がいきなり全社最高レベルになる
  • レガシーアプリの接続: OIDCを話せない古いアプリのために、リバースプロキシで認証を肩代わりする構成(いわゆる認証プロキシ、oauth2-proxyなど)が必要になる。これは第2部のAPI Gatewayの原型でもある

Build vs Buy

選択肢向いているケース注意点
SaaS (Auth0, Okta, Entra ID)認証がコア差別化でない大多数の企業MAU課金が規模で効いてくる。カスタムフローの自由度に限界
OSSセルフホスト (Keycloak)要件が特殊、データ主権要件、コスト最適化運用・アップグレード・可用性を自分で背負う。専任者なしでは事故る
自作ほぼ推奨しない認証は「動く」と「安全」の距離が最も遠い領域。トークン発行・失効・鍵ローテーションを正しく作るのは想像の数倍難しい

「自作しない」はほぼ鉄則だと考えている。OAuth/OIDCの仕様群はRFCが数十本絡み合っており、既知の攻撃パターン(mix-up attack、code injection等)への対策が実装品質に依存する。車輪の再発明というより、ブレーキの再発明に近い。

組織面: 「認証基盤チーム」の誕生と統制の始まり

Stage 1は技術だけでなく組織の転換点でもある。IdPを運用する以上、誰かがオーナーになる必要があり、ここでプラットフォームチーム(あるいは情シス/セキュリティチーム)が認証の統制権を持つ構造が生まれる。

これは統制面ではメリットだ(ポリシーの一元適用、監査対応)。一方で、各アプリチームから見ると「新しいアプリを作るたびにクライアント登録を申請する」「ログイン画面のカスタマイズは基盤チームの優先度次第」という依存とボトルネックが生まれる。Conwayの法則の逆作用——アーキテクチャが組織のコミュニケーション構造を規定し始める——であり、この緊張関係は第2部のAPI Gateway、マイクロサービスでさらに先鋭化する。

セルフサービス化(クライアント登録の自動化、Dynamic Client Registration、Terraform管理)をどこまで進めるかが、統制と自律のバランスを決める最初の設計判断になる。

Stage 0→1 の難易度と判断基準

  • 技術難易度: 中。プロトコル自体は枯れており、既製品を使えば構築は難しくない。難所はデータ移行・レガシー接続・ログアウト整合性
  • 組織難易度: 中〜高。全アプリチームを巻き込む横断プロジェクトになる。トップダウンの推進力がないと「新規アプリだけIdP、既存は塩漬け」で中途半端に止まりがち
  • 導入すべきシグナル: アプリが2つ以上ある、モバイル対応が必要、MFAを求められた、監査でパスワード管理を指摘された——このどれかが出たら検討開始のタイミングだと思う

逆に、単一アプリで当面その予定もないなら、セッションCookieのままで良い。OIDCにすること自体には価値がなく、課題がないのに導入するのは負債である。


第2部(API Gatewayとマイクロサービス)では、サービス数が増えたときに「各サービスがバラバラにトークン検証する」構成がなぜ破綻するか、API Gatewayによる統制と、マイクロサービスでのトークン中継・サービス間認証のパターンを扱う。