Amazon Cognitoを利用したIDPとのシングルログアウト連携の実現

複数のWebサービスに1つのIDでシングルサインオン (SSO)・シングルログアウト (SLO) できるように、IDプロバイダ (IDP) を構築して各サービスとOpenID Connect (OIDC) やSAMLで連携する構成について考える。このときに、IDPと連携するWebサービスの構築にAWS（Amazon API GatewayやAWS AppSyncなど）を利用する方法を検討した。

IDPと連携してAWSサービスの認証を行うにはAmazon Cognitoを使うのが常套手段となる。SSOのみが要件であれば公式ドキュメントや解説記事も多く悩むポイントは少ないが、SLOの実現についてはあまり取り上げられていない。この記事ではAmazon CognitoでIDPと連携する際のSLOの実現方法を整理する。

### 前提

* IDPと各サービスは異なるドメイン（オリジン）で提供される。
* IDPと各サービスはOpenID Connect (OIDC) もしくはSAMLで連携する。
* AWSサービスとIDPの連携は以下のいずれかの方法で行う。
  * Amazon Cognito User Pools (Hosted Web UI) の[フェデレーション機能](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-identity-federation.html)
  * Amazon Cognito Federated Identity Poolsの[外部IDP連携機能](https://docs.aws.amazon.com/cognito/latest/developerguide/external-identity-providers.html)
* SafariやFirefoxにてデフォルトで制限されることとなった、Front-Channelで3rd Partyコンテキストを利用する仕組み（OIDCの[Front-Channel Logout](https://openid.net/specs/openid-connect-frontchannel-1_0.html)など）は採用しない。

### SLO実現方法

Amazon Cognito User Poolsを使う場合とAmazon Cognito Identity Poolsを使う場合のそれぞれについて、SLOを実現する方法と制限となることを説明する。

#### Amazon Cognito User Pools

* サービス起点のSLO（サービスからログアウトしたときにIDPからもログアウトする）
  * IDPとSAMLで連携する場合は、User PoolのSAML連携設定でSLOを有効化すればよい。
      * 設定を有効化すると、User Poolの[LOGOUT Endpoint](https://docs.aws.amazon.com/cognito/latest/developerguide/logout-endpoint.html)呼び出し時に、User Pool (Hosted UI) のセッションが削除された後でIDPにリダイレクトされ（HTTP Redirect Bindingで）SLOリクエストが伝達される。
      * 参考：[Creating and Managing a SAML Identity Provider for a User Pool (AWS Management Console)](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-managing-saml-idp-console.html)
  * IDPとOIDCで連携する場合は、User PoolはOIDCでのSLO機能をサポートしていないため、User Poolのログインセッションを無効化後の[LOGOUT Endpoint](https://docs.aws.amazon.com/cognito/latest/developerguide/logout-endpoint.html)からのリダイレクトを受けて、OIDCのRP-Initiated Logoutを行う処理をサービス側に（もしくはIDPの機能として）作り込む必要がある。
      * 参考：[OpenID Connect RP-Initiated Logout 1.0 - draft 01](https://openid.net/specs/openid-connect-rpinitiated-1_0.html)

* IDP起点のSLO（IDPからログアウトしたときにサービスからもログアウトする）
  * リアルタイムでのSLOを実現するには、IDPでのログアウト処理にてブラウザをUser PoolのLOGOUT Endpointに一度リダイレクトする必要がある。（IDP側に作り込みが必要となる。）
      * OIDC連携、SAML連携のどちらの場合でも、Back-Channel（ブラウザ経由ではなくバックエンド間の通信）を使ってIDPからUser Poolにログアウト要求を送る方法は提供されていない。
      * User Poolにはトークン発行等のタイミングでLambdaを実行する[Lambdaトリガー](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools-working-with-aws-lambda-triggers.html)を設定する機能があるが、トリガー処理ではIDPとのフェデレーションに関する情報にアクセスできないため、ここでIDPのセッション状態を確認することはできない。このため、IDPのセッションが無効な場合にトークン発行をエラーにする、といった作り込みも実現できない。
      * User Pool経由でログインするサービスからは、IDP側のログインセッションに関する情報にアクセスできない（完全に隠蔽される）ため、サービス側でIDPのセッション状態を監視する機能を作り込むこともできない。
* その他の制限
  * User Poolが発行するアクセストークン・リフレッシュトークンは、ログインセッションとライフサイクルが分離されており、たとえ前述の方法でUser Poolのログインセッションを削除しても有効期限まで有効となる。
  * また、User Poolが発行するアクセストークン・リフレッシュトークンをログインセッション単位で無効化する機能も提供されていない。（ユーザーに紐づく全てのトークンを無効化する[GlobalSignOut API](https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_GlobalSignOut.html)が提供されているがログインセッション単位での削除はできない。）

#### Amazon Cognito ID Pools

* ID Poolsを利用する場合は、サービスとIDPは直接フェデレーション連携を行うこととなる。
  * ID Poolsの役割はあくまで、IDPが発行するアサーション（OIDCではIDトークン、SAMLではSAMLアサーション）を検証して、AWSサービスの認証に利用できる[Temporary Credential](https://docs.aws.amazon.com/cognito/latest/developerguide/getting-credentials.html)を発行するまでとなる。
* サービスとIDPのSLOについても通常のOIDC、SAML連携と同様に作り込めばよいが、[Temporary Credential](https://docs.aws.amazon.com/cognito/latest/developerguide/getting-credentials.html)（有効期限は最短でも1時間）は個別に無効化ができない。