Relying Party(mod_auth_openidc)とOpenID Provider(OpenAM)のセッションを消す(シングルログアウトする)

## 今日やること

タイトルの通りです（笑

OpenAMのセッションは、通常`iPlanetDirectoryPro`という名前のドメインCookie(SSO Token)で保持されます。Policy Agentを使ったシングルサインオンの場合は、このSSO TokenがOpenAMとPolicy Agentが導入されたアプリケーションの双方のセッションを保持する役目を負っています。ゆえに、SSO TokenをOpenAM上でInvalidationすれば、シングルログアウトができます。

ただし、Federationでシングルサインオンした場合はそうはいきません。Relying PartyはOpenID Providerから連携されたAssertion(ID Token)をVerifyし、**自分でも認証したことにする**ために、セッションを払い出します。この場合、シングルログアウトを実現するには、**Relying Partyのセッション**と**OpenAMのセッション**、さらには**ID Token（やAccess Token）**をInvalidationする必要があります。

## mod_auth_openidcのセッション

mod_auth_openidcはOpenID Providerから連携されたID TokenのVerifyが終わると、自分のセッションを払い出します。下記はOpenID Providerで認証後、Relying Partyにアクセスが完了した後のCookie一覧です。`mod_auth_openidc_session`という名前のCookieがそれに当たります。

<img width="451" alt="001.JPG" src="https://qiita-image-store.s3.amazonaws.com/0/148404/cff3791d-9ae1-23f2-42ee-4f6928564d24.jpeg">

## OpenID Connectにおけるセッション関連の仕様

2016年12月時点で、未だドラフトです。今年は8月に更新されて以降音沙汰なしといった感じです。

* [OpenID Connect Session Management 1.0 - draft 27](http://openid.net/specs/openid-connect-session-1_0.html)

この中の[5.  RP-Initiated Logout](http://openid.net/specs/openid-connect-session-1_0.html#RPLogout)を今日は試していきます。このRelyig Party Initiated Logoutというのは簡潔に言うと、**Relying Partyが起点となってセッション(RPとOP)とトークン(ID Tokenなど)を消していく**といったものです。

## mod_auth_openidcにおけるRP Initiated Logoutの設定

mod_auth_openidcはRP Initiated Logoutの仕様に対応しています。以下のような流れで実行されます。

1. Relying Party上のRedirect URIのQuery Stringに`logout`というパラメータを載せて、リクエストを送る
2. Relying Partyのセッション(mod_auth_openidc_session)がInvalidationされる
3. Relying PartyはOpenID Providerのend_session_endpointに**ID Token**と**Invalidation後にリダイレクトするURL**をリクエストする
4. OpenID Providerはセッション(iPlanetDirectoryPro)とトークン(ID Tokenなど)をInvalidationしてから、**Invalidation後にリダイレクトするURL**にリダイレクトする
5. **Invalidation後にリダイレクトするURL**はシングルログアウト完了画面にしておく

このend_session_endpointは3回ほど前の記事でOpenID Provider(OpenAM)のDiscovery Endpoint(/openam/oauth2/.well-known/openid-configuration)をたたいたときのレスポンスに含まれています。

では設定をしていきましょう。

### OpenID Providerの設定

前々回登録したRelying Partyの設定を以下に変更します。

|param|value|option|
|:--|:--|:--|
|名前|relyingparty|変更なし|
|パスワード|password|変更なしclient secret|
|リダイレクトURI|https://rp.example.com:443/protected/redirect_uri|redirect_uriのパスを変更|
|スコープ|openid profile|変更なし|
|ポストログアウトリダイレクト URI|https://rp.example.com:443/logout.html|追加する|

このポストログアウトリダイレクトURIというのは先ほどの**Invalidation後にリダイレクトするURL**を指します。Open Redirectorにならないように、ちゃんとValidationします。

### Relying Partyの設定

mod_auth_openidcの設定も少し変更します。下記は`/etc/httpd/conf.d/openidc.conf`です。

```apache
LoadModule auth_openidc_module modules/mod_auth_openidc.so                                                                                                                                                          
OIDCProviderMetadataURL https://iam.example.com:443/openam/oauth2/.well-known/openid-configuration                                                                                                                  
OIDCClientID relyingparty                                                                                                                                                                                           
OIDCClientSecret password                                                                                                                                                                                           
OIDCRedirectURI https://rp.example.com:443/protected/redirect_uri                                                                                                                                                   
OIDCCryptoPassphrase passphrase                                                                                                                                                                                     
OIDCScope "openid profile"                                                                                                                                                                                          
OIDCSSLValidateServer Off                                                                                                                                                                                           
<Location /protected>                                                                                                                                                                                               
  AuthType openid-connect                                                                                                                                                                                           
  Require valid-user                                                                                                                                                                                                
</Location>
```

変更したのは、`OIDCRedirectURI`だけです。設定変更後はApacheを再起動しましょう。

```bash
$ sudo systemctl restart httpd.service
```

### コンテンツの配置

下記のコンテンツを`/var/www/html`以下に配置します。

```bash
$ tree
.
├── logout.html
└── protected
    └── logoutlink.html

1 directory, 2 files
```

* logout.html

```html
<h1>YOU HAVE BEEN LOGGED OUT.</h1>
```

* logoutlink.html

```html
<a href="https://rp.example.com:443/protected/redirect_uri?logout=https://rp.example.com:443/logout.html">Logout</a>
```

繰り返しになりますが、logout.htmlは**Invalidation後にリダイレクトするURL**です。logout.linkはend_session_endpointにリクエストを送るためのリンクを作っています。

## 動作確認

まず、Relying Partyのlogout.linkにアクセスすると、OpenID ConnectのAuthorization Code Flowが開始されます。

前回作ったアカウント（ユーザー名は`gnanashi`）でサインインします。

<img width="397" alt="002.JPG" src="https://qiita-image-store.s3.amazonaws.com/0/148404/7f81cbcb-b9ec-91a8-2e04-d5e0a6687ca1.jpeg">

ユーザー属性の連携同意画面が表示されるので、同意します。

<img width="383" alt="003.JPG" src="https://qiita-image-store.s3.amazonaws.com/0/148404/0152ae81-f1f7-90d0-6008-6845d2ec585b.jpeg">

シングルログアウトリンクが表示されますので、クリックし、end_session_endpointにログアウト要求を送信します。

<img width="388" alt="004.JPG" src="https://qiita-image-store.s3.amazonaws.com/0/148404/8f268fff-dcfa-52f6-05b7-38b0ce0d717f.jpeg">

その後、logout.htmlのコンテンツが表示されます。

<img width="387" alt="005.JPG" src="https://qiita-image-store.s3.amazonaws.com/0/148404/47aba3bb-45cf-1bfb-e02c-408f9451384b.jpeg">

この時点で、Relying Partyのセッション(mod_auth_openidc_session)、OpenID Providerはセッション(iPlanetDirectoryPro)とトークン(ID Tokenなど)のInvalidationは完了しています。表示されていますが、OpenID Provider(OpenAM)上では無効なセッションとして扱われます。

<img width="646" alt="006.JPG" src="https://qiita-image-store.s3.amazonaws.com/0/148404/7ba961b5-71f9-0896-8705-81f7bf1aed0b.jpeg">

ちなみに、end_session_endpointには下記のようなリクエストが送られています。RP-Initiated Logoutの仕様通りですね。

```
GET
/openam/oauth2/connect/endSession?
    id_token_hint=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
    .
    eyJhdF9oYXNoIjoiTURxUlYyUGFlN3R1M3F1bGdPaFFjdyIsInN1YiI6ImduYW5hc2hpIiwiYXVkaXRUcmFja2luZ0lkIjoiZDcxNDQ0ODEtNmY3Ny00OTM5LTk0ZDYtYzViZjEzYzZiMmRkLTkzNTgiLCJpc3MiOiJodHRwczovL2lhbS5leGFtcGxlLmNvbTo0NDMvb3BlbmFtL29hdXRoMiIsInRva2VuTmFtZSI6ImlkX3Rva2VuIiwibm9uY2UiOiI1VGdaYW5xM29fTkMwZWRGcmZ2UF85X2hJRGx2VXdGbDFUcUdzUWd5WGVBIiwiYXVkIjoicmVseWluZ3BhcnR5IiwiY19oYXNoIjoibnhEOTAxOHVYdWlpTjdpM2FIYTRyUSIsIm9yZy5mb3JnZXJvY2sub3BlbmlkY29ubmVjdC5vcHMiOiJhNmVjOWQ3Zi01MGQ5LTRjZTYtYjdkNi01ZDMxOWE0ZWEyYjUiLCJhenAiOiJyZWx5aW5ncGFydHkiLCJhdXRoX3RpbWUiOjE0ODE0MjEwNDcsInJlYWxtIjoiLyIsImV4cCI6MTQ4MTQyNDY4OCwidG9rZW5UeXBlIjoiSldUVG9rZW4iLCJpYXQiOjE0ODE0MjEwODh9
    .
    Kcuqe1YXTUuG30GAYcM8m1-Mkt75s37ehrEGFviPftg
    &
    post_logout_redirect_uri=https%3A%2F%2Frp.example.com%3A443%2Flogout.html
```

## まとめ

* Federationでシングルサインオンしたとき、シングルログアウトを実現するのはちょっと難しい
* mod_auth_openidcはRP-Initiated Logoutができる
* OpenID Provider側のend_session_endpointはDiscovery Endpointでパスが公開されている

~~長々と書いたけど、ぶっちゃけブラウザ落とせばいいんじゃないの？~~

予定とは何だったのか状態になってきましたね～
明日はなにしよう～(◞‸◟)

ばい！