KeycloakでMattermost(Team Edition)にSSOログインする

# はじめに

Slack cloneとして有名な[Mattermost](https://github.com/mattermost/mattermost-server)ですが、完成度は非常に高いのですが残念ながらSAMLによるSSOに関しては有償版のEnterprise E20でないと提供されていません。フリー版のTeam EditionやEnterprise E10を使ってSSOしたい場合は、下記の記事などで紹介されているとおり、[GitLab](https://gitlab.com/)と組み合わせる必要がありました。

* [KeycloakのSAML SSOでGitLabやMattermostにログインする](https://int128.hatenablog.com/entry/2017/12/22/205244)

MattermostはGitLabを使ってのログイン機能であれば全エディションで利用可能なため、

```
Mattermost --GitLabログイン機能--> GitLab --SAMLでログイン--> Keycloak
```

というログイン連携ができるわけです。

GitLabを使っているところならこれでいいのですが、Mattermostだけ使いたいんだよ、という場合はこれだと困ります。MattermostはOSSなのでソース改修すれば対応できるでしょうが、改修なしで何とか認証連携できないか？と探ってみたところ、実現方式を思いついたので今回紹介したいと思います。

# 方式

実はMattermostのGitLabログイン機能は、連携の際のHTTPリクエスト/レスポンスを見ると、単にOAuth2.0の認可コードフローでID連携しているということが分かります。つまり、OAuth2.0に対応した認可サーバを使えば別にGitLabでなくても連携ができてしまう可能性があります。これにより、Keycloakを使えば

```
Mattermost --GitLabログイン機能(OAuth2.0)--> Keycloak
```

という構成がとることができます。ただし、いくつか注意点がありますので設定方法とともに説明します。

# 動作確認したバージョン

* Keycloak: 3.4.3.Final
* Mattermost: 4.10.0

# 設定方法

前提として、Keycloakが既にインストール済みで、連携用に`sample`レルムが作成済みの状態、とします。Keycloakのインストールは[Keycloakのセットアップ](https://qiita.com/tamura__246/items/13fc301e9409fef77bf3)を、レルムの作成については[KeycloakでOpenID Connectを使ってみる](https://qiita.com/tamura__246/items/4290a1035e1adcab733b#11-%E3%83%AC%E3%83%AB%E3%83%A0%E3%81%AE%E4%BD%9C%E6%88%90)などの記事を参考にするとよいでしょう。

MattermostとのSSOの設定手順としては、

1. KeycloakにMattermost用のクライアントを登録する
    1. クライアントの登録
    2. Mapperの編集
    3. テスト用ユーザの登録
2. MattermostにGitLab認証を設定する

となります。以下、詳細を説明していきます。なお、注意点として今回はすべてHTTPで通信しておりHTTPSを利用していませんが、実環境ではセキュリティのためHTTPSを使うようにしてください(OAuth2はHTTPSであることを前提としているため)。

# KeycloakにMattermost用のクライアントを登録する

## クライアントの登録
KeycloakにSSO対象のアプリケーションを登録する場合は、**クライアント**と呼ばれるものを登録する必要があります。また、Keycloakにはこのクライアントのプロトコルとして**OpenID Connect**と**SAML**の二種類があります。MattermostのGitLabログイン機能はOAuth2を利用しているため、作成するクライアントのプロトコルには`openid-connect`を選択します(OpenID ConnectはOAuth2をベースとしているため、OAuth2を喋ることもできます)。

Keycloakの管理コンソールにログインし、`sample`レルムにて画面左メニューの`Configure > Clients`からクライントを登録します。ポイントとなる設定箇所は下記のとおりです。

* Standard Flow Enabled: `ON`
* Access Type: `confidential`
* Valid Redirect URIs: `http://<MattermostのFQDN>/signup/gitlab/complete`

![mattermost-client.png](https://qiita-image-store.s3.amazonaws.com/0/35233/0bffe406-1edd-eb79-33ab-94a7a5e65d12.png)


また、`Credential`タブを開き**Secret**の値をコピーしておきます。この値とクライアント登録時に設定した**Client ID**の値をMattermost側に後ほど設定します。

## Mapperの編集
MattermostにSSOログインする際に、MattermostはOAuth2のアクセストークンを利用してKeycloakのUserInfoエンドポイントにアクセスし、そのレスポンス値に含まれるユーザ情報を利用してMattermostにログインする仕組みとなっています。Mattermost側としては、返されるユーザ情報としては[以下のGo言語の構造体](https://github.com/mattermost/mattermost-server/blob/v4.10.0/model/gitlab/gitlab.go#L19-L25)で表現されるJSONデータを期待しています。

```
type GitLabUser struct {
	Id       int64  `json:"id"`
	Username string `json:"username"`
	Login    string `json:"login"`
	Email    string `json:"email"`
	Name     string `json:"name"`
}
```

よって、KeycloakのUserInfoエンドポイントでは上記項目を返すように設定を行う必要があります。KeycloakではMapperと呼ばれる機能で返す値を細かくカスタマイズできますので、上記構造体に合うように設定していきます。デフォルトだと、以下のような内容でKeycloakはユーザ情報を返します。

```json
{
	"sub": "c092a81a-1835-493f-95a7-46c7b8d4dff1",
	"name": "test test",
	"preferred_username": "test",
	"given_name": "test",
	"family_name": "test",
	"email": "test@example.org"
}
```

そのため、KeycloakのMapperの設定にて

* `preferred_username` => `username` に変更
* `id` 項目を新規に追加

を行います。`login`項目はMattermost側は必須ではないようなので(`username`がない場合に[代わりに使用される](https://github.com/mattermost/mattermost-server/blob/v4.10.0/model/gitlab/gitlab.go#L35-L36))、今回はなしとしています。

作成したクライアントの`Mappers`タブを開き、以下の設定を行います。

1. `username`の設定を開き、Token Claim Nameの設定値を`username`に変更する
2. 新規にMapperの追加を下記設定項目で行う
    * Name: `mid`
    * MapperType: `User Attribute`
    * User Attribute: `mid`
    * Token Claim Name: `id`
    * Claim JSON Type: `long`
    * Add to userinfo: `ON`

Nameは任意名称で構いません。User AttributeはMattermost側のアカウントとリンクするために使われるIDを格納するための、ユーザのカスタム属性名となります。今回は`mid`(Mattermost Id)としました。

## テスト用ユーザの登録
SSOログインテスト用にユーザをKeycloakの管理コンソールから登録しておきます。

* Email
* First Name
* Last Name

がMattermostで使用される属性なのでこれらを入力しておきます。

また、アカウントリンク用のカスタム属性(今回の設定例だと`mid`属性)も設定しておきます。値は、**1以上**としておきます([0はMattermost側で無効と判断される](https://github.com/mattermost/mattermost-server/blob/v4.10.0/model/gitlab/gitlab.go#L78-L79)ので注意)。

### 注意点
このカスタム属性が他のユーザと被ってしまった場合、MattermostへSSOログインのタイミングでMattermost側のユーザテーブルの同IDのレコードが存在すると上書きされてしまうようです。よって、実運用においてはこの値が被らないようユーザごとに必ず一意になる仕組みを考える必要があります。この対応が地味に面倒な予感がします。

# MattermostにGitLab認証を設定する

MattermostのSystem Console > AUTHENTICATION > GitLab から連携設定を有効化します。用意した**Client ID**と**Secret**をここで設定します。また、ここでOAuth2のエンドポイントURL設定を行うのですが、`GitLab Site URL`しか入力できず、エンドポイント単位に細かく設定できません。そのため、Keycloakが提供する形式のOAuth2エンドポイントURLを設定することができません。

![mattermost-auth-gitlab.png](https://qiita-image-store.s3.amazonaws.com/0/35233/cd163cea-08a6-0498-fcf0-0a58b4ca1110.png)


対応策として、以下の2方式を思いつきました。どちらでも対応可能でした。

## A) Keycloakの前段にリバースプロキシを配置しURL置換を行う

例えばApache HTTPD Serverを使う場合は、`mod_rewrite`を使って以下のように設定すればOKです(Apacheと同じサーバでKeycloakを8080ポートで動かしている場合)。

```
RewriteEngine On
RewriteRule ^/auth/realms/(.*)/api/v4/user http://localhost:8080/auth/realms/$1/protocol/openid-connect/userinfo [P,L]
RewriteRule ^/auth/realms/(.*)/oauth/authorize http://localhost:8080/auth/realms/$1/protocol/openid-connect/auth [P,L]
RewriteRule ^/auth/realms/(.*)/oauth/token http://localhost:8080/auth/realms/$1/protocol/openid-connect/token [P,L]
```

## B) GUIで設定せずMattermostの設定ファイルを直接編集する

MattermostではGitLabログインの設定は[config.jsonファイル](https://docs.mattermost.com/administration/config-settings.html#gitlab)で設定できるようになっています。GUIで設定せず、このファイルを直接編集することで細かくOAuth2エンドポイントURLを設定することができます。

例えば、Keycloakの`sample`レルムでSSOログインさせるには、以下のような設定になります。

```json
    "GitLabSettings": {
        "Enable": true,
        "Secret": "694ce9b7-9bed-4961-9ad2-8c680a9e1daa",
        "Id": "mattermost",
        "Scope": "",
        "AuthEndpoint": "http://sso.example.org/auth/realms/sample/protocol/openid-connect/auth",
        "TokenEndpoint": "http://sso.example.org/auth/realms/sample/protocol/openid-connect/token",
        "UserApiEndpoint": "http://sso.example.org/auth/realms/sample/protocol/openid-connect/userinfo"
    },
```

# 動作確認

Mattermostの画面にアクセスし、画面下部に表示される**Sign in with**から**GitLab**をクリックします。GitLabの代わりにKeycloakを設定しているため、Keycloakのログイン画面が表示されますので、登録しておいたテスト用ユーザでログインします。ログイン後Mattermostにリダイレクトで戻りますが、OAuth2の設定が正しくされていれば無事にSSOログインできているはずです。

本当は「GitLab」という文字列の代わりに「Keycloak」とか任意名称に変えれると良いですが、MattermostはUIカスタマイズができるっぽいのでそちらで対応可能かもしれませんね。

# まとめ

Mattermostのフリー版のTeam Editionでも実現可能なSSOの設定方法について紹介しました。今回はSSOサーバにKeycloakを利用していますが、OAuth2に対応している認可サーバでUserInfoエンドポイントがあり(つまり、OpenID Connectに対応していれば、ですね)、返すユーザ属性をMattermost側が期待する内容にカスタマイズできるのであれば、おそらく連携できるのではないかと思います。