1. はじめに
前回の記事「OpenID Connectとは何か? OAuth 2.0に「ログインの証明」を足す仕組み」では、OAuth 2.0の認可の枠組みに、標準化された認証の仕組みを追加するOpenID Connect(OIDC)を扱いました。しかし、このシリーズでまだ触れていない前提があります。「Googleでログイン」できるサービスは、Google自体を含めて無数にありますが、毎回Googleに対して個別にログイン操作をやり直しているわけではありません。この、複数サービスをまたいだ認証の共有を実現する仕組みが、SSO(Single Sign-On)です。
2. この記事はこんな方におすすめ
- SSOという言葉は知っているが、仕組みを説明できない方
- SAMLとOpenID Connectの違いが気になる方
- 「SSO=OIDC」だと思っている方
3. 内容
SSOはOIDCではない
まず整理しておきたいのは、SSOは特定の技術やプロトコルの名前ではなく、「1度の認証で、複数のサービスを利用できるようにする」という認証のパターンそのものを指す言葉だという点です。これを実現する具体的な手段として、代表的なものにSAML(Security Assertion Markup Language)と、OpenID Connectがあります。
認証基盤と各サービスの役割
SSOの仕組みを理解する上で、まず混乱しやすいのが用語です。OIDCとSAMLでは、同じ役割を指す言葉が異なります。
- 本人確認を行い、認証結果を発行する側:OIDCでは「OpenIDプロバイダー」、SAMLでは「アイデンティティプロバイダー」と呼ばれます
- 認証結果を受け取り、利用者にサービスを提供する側:OIDCでは「リライングパーティ」、SAMLでは「サービスプロバイダー」と呼ばれます
呼び方は違いますが、役割そのものは共通しています。「利用者の身元を一手に引き受けて確認する信頼できる相手」と、「その確認結果を信じて利用者を受け入れる、複数の個別サービス」という構造が、SSOの土台になっています。
ここで1つ、誤解しやすい点を補足しておきます。ここでいう「認証の共有」は、すべてのサービスで同じCookieやセッションIDを使い回す、という意味ではありません。今回扱うSSOでは、認証基盤と各サービスが、それぞれ別のセッションを管理します。認証基盤での認証結果を各サービスが検証して受け入れ、各サービスは自分自身のセッションを確立する、という構造です。利用者から見ると1度のログインで済んでいるように感じられますが、実際には「ログイン操作が省略されている」のであって、各サービス内部の状態まで1つに統合されているわけではありません。
SAML 2.0はXML、OIDCはJWT
SAMLとOIDCの実装上の大きな違いは、認証結果をどう表現するかです。
- SAML 2.0:2005年にOASISで標準化された、XMLベースの規格です。IdPは認証結果などを「SAMLアサーション」に記載します。SPは、アサーションやそれを包むレスポンスの署名、発行元、受取先、有効期間などを、利用する方式に応じて検証し、受け入れます
- OpenID Connect:以前扱った通り、認証結果はIDトークンというJSON形式のJWTで表現されます
SAMLは歴史が長く、企業内のシステム連携で広く使われてきました。一方OIDCは、OAuth 2.0を土台にした軽量な設計で、モバイルアプリやAPI中心の現代的なアーキテクチャに向いているとされ、コンシューマー向けのSSOで広く採用されています。どちらか一方が技術的に優れているというより、想定されている用途や、既存のシステム構成との相性で選ばれる傾向があります。
実際の流れ:OIDCを使ったサービス起点のSSO
ここでは、これまでのシリーズで扱ってきたOIDCに絞って、具体的な流れを見てみます。
- 利用者がサービスAへアクセスし、サービスAでのログイン状態がなければ、ブラウザがOPへリダイレクトされる
- OPは自身のセッションを確認し、必要な場合に利用者へログイン操作を求める
- 認証・認可の条件が満たされると、OPは認可コードを返して、ブラウザをサービスAへ戻す
- サービスAは認可コードを使ってIDトークンなどを取得し、必要な検証を行ったうえで、自分のサービス用のセッションを確立する
- 別のサービスBを利用するときも、サービスBは同じOPへ認証を要求する。OPの既存セッションを利用できれば、利用者のログイン操作を繰り返さずに、サービスB用の認証処理を進められる
ここで重要なのは、省略されるのは「ログイン操作」であって、「認証結果の取得・検証」までではないという点です。ステップ5でも、サービスBは改めてOPへ認証を要求し、IDトークンを取得・検証したうえで、自分自身のセッションを確立します。ただし、OPのセッションが有効でも、サービスがprompt=loginで再認証を要求した場合や、前回の認証からの経過時間がmax_ageで指定された上限を超えた場合などには、再認証が求められます。また、認証に成功したことと、そのサービスの利用権限があることは別の話なので、各サービス側での権限確認も必要です。
ログアウトは「1つ消して終わり」ではない
SSOには、ログインだけでなく、もう一つ厄介な課題があります。ログアウトです。
複数のサービスにまたがって認証を共有しているということは、利用者が「ログアウト」をしたいときに、どこまでの範囲をログアウトさせるべきかが曖昧になる、ということでもあります。ある1つのサービスだけでログアウトしても、多くの実装では、そのサービス自身のセッションが切れるだけで、認証基盤側のセッションや、他のサービスのセッションはそのまま残り続けます。
複数サービスのログアウトを連動させるため、SAML 2.0にはシングルログアウト(Single Logout)が、OIDCにはRP-Initiated Logout(サービスからOPへログアウトを要求する)、Front-Channel Logout(ブラウザ経由でOPから各サービスへ通知する)、Back-Channel Logout(サーバー間で直接通知する)といった、関連する仕様が定義されています。ただし、これらは認証基盤と各サービスの双方が対応し、必要な設定を行っていることが前提です。通知方法には、ブラウザを経由するものと、サーバー間で直接通信するものがあり、それぞれ得意・不得意があります。Back-Channel Logoutでは、利用するOPから、各RPのログアウト用エンドポイントへ直接通信できる必要があります。そのため、ファイアウォールなどを含めたネットワーク構成も考慮しなければなりません。
こうした事情から、「一か所からログアウトすれば、必ず全サービスから同時にログアウトできる」とは限りません。ブラウザ側の制限や通信障害などによって、通知が一部のサービスに届かないこともあります。実際、SAMLの仕様にも、参加者全員へのログアウトが完了しなかった状態を表すPartialLogoutが定義されているほどです。SSOを設計する際には、どのセッションを終了させるかに加えて、一部のログアウトに失敗した場合の扱いまで、あわせて考えておく必要があります。
4. まとめ
SSOは、一度の認証を複数サービスの利用につなげる仕組みです。SAMLやOIDCを使う構成では、認証基盤の認証結果を各サービスが検証して受け入れますが、各サービスのセッションまで一つになるわけではありません。そのため、ログアウトをどこまで連動させるかは、別途設計する必要があります。
扱う範囲は一つのサイトから複数サービスへ広がっても、考えるべきことは「ログイン状態を、誰が、どこまで、どう信頼するか」なのだと感じました。便利にログインできる仕組みと、その状態を安全に終わらせる仕組みは、セットで考える必要があるのだと思います。
参考
- Assertions and Protocols for the OASIS Security Assertion Markup Language (SAML) V2.0 - OASIS
- OpenID Connect Core 1.0 incorporating errata set 2 - OpenID Foundation
- OpenID Connect RP-Initiated Logout 1.0 - OpenID Foundation
- OpenID Connect Front-Channel Logout 1.0 - OpenID Foundation
- OpenID Connect Back-Channel Logout 1.0 incorporating errata set 1 - OpenID Foundation
- SAML vs OAuth vs OIDC: key differences explained - Cisco Duo