coreos/dex使ってみた

# はじめに
Go製の OAuth2.0 / OpenID Connect Provider実装である [coreos/dex](https://github.com/coreos/dex/) を使ってみたメモです。

基本的に公式ドキュメントのチュートリアル＋αの補足説明レベルです。

dexはOpenID ConnectでいうところのOPに相当するサーバですが、IdPの部分は持っていない認証proxy的なものです。
ちなみに同じようにOAuth2.0 / OpenID Connect Provider実装である [ory/hydra](https://github.com/ory/hydra)を使ってみたメモは以下。

[ory/hydraで OAuth2.0 / OpenID Connect Provider を立ててみる](https://qiita.com/minamijoyo/items/075b1e9b6a249f8008a8)

# インストール
以下の公式チュートリアルに従って、作業します。
https://github.com/coreos/dex/blob/master/Documentation/getting-started.md

go getしてきて自分でmakeします。Goの開発環境の設定などは割愛。

```
$ go get github.com/coreos/dex
$ cd $GOPATH/src/github.com/coreos/dex
$ make
```

```
[dex@master]$ ./bin/dex version
dex Version: fe1516332c4a41aee1dcab66164660676527a36c
Go Version: go1.9
Go OS/ARCH: darwin amd64
```

dexのバージョン番号がコミットIDになってますが、これを書いてる時点で最新のリリースはdex v2.7.1です。
ちなみにdexはv1とv2でいろいろ変わってるようですが、masterブランチで作業しているのでv2の認識。
v1とv2の違いは以下のドキュメントを参照。

https://github.com/coreos/dex/blob/master/Documentation/v2.md

大きなところで「Local user management」を削除していて、IdPの機能をなくして完全に認証proxyになった印象。[ory/hydra](https://github.com/ory/hydra) よりも [bitly/oauth2_proxy](https://github.com/bitly/oauth2_proxy) のイメージに近くなった気がする。ちなみにoauth2_proxyにも最近OpenID Connect対応が入ったよう。ただ、dexの方はLDAPとかSAML2.0もサポートしているので、もっと守備範囲の広い認証proxy。

# 起動
デモ用のサンプルのファイルが提供されているので、これを使ってサーバを起動します。

```
[dex@master]$ bin/dex serve examples/config-dev.yaml
time="2017-10-03T03:22:32Z" level=info msg="config issuer: http://127.0.0.1:5556/dex"
time="2017-10-03T03:22:32Z" level=info msg="config storage: sqlite3"
time="2017-10-03T03:22:32Z" level=info msg="config static client: example-app"
time="2017-10-03T03:22:32Z" level=info msg="config connector: mock"
time="2017-10-03T03:22:32Z" level=info msg="config connector: local passwords enabled"
time="2017-10-03T03:22:32Z" level=info msg="keys expired, rotating"
time="2017-10-03T03:22:33Z" level=info msg="keys rotated, next rotation: 2017-10-03 09:22:33.136007551 +0000 UTC"
time="2017-10-03T03:22:33Z" level=info msg="listening (http) on 0.0.0.0:5556"
```

デモ用のクライアントアプリを起動します。

```
[dex@master]$ ./bin/example-app
2017/10/03 14:14:32 listening on http://127.0.0.1:5555
```

ブラウザでクライアントアプリ `http://127.0.0.1:5555` にアクセスします。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/61152/ca10eec8-673a-b7ac-3868-c1a24c7f0db4.png)

「Login」ボタンを押すとdexサーバにリダイレクトされます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/61152/800db1f1-f981-9e1f-2952-5c2e43c8e421.png)

「Login with Email」ボタンを押します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/61152/6a7c14af-d081-8535-e4f8-f52481eb2913.png)

Usernameに `admin@example.com` をPasswordに `password` を入力して 「Login」ボタンを押します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/61152/cd102f8a-1cf8-b5bc-25f0-f1fc8231dfe3.png)

認可の同意画面がでてきました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/61152/390abc29-169d-4b96-74f2-0a93a773c48b.png)

「Grant Access」を押します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/61152/2620a3c3-ffa4-8084-d649-aead5746c185.png)

OpenID ConnectのID TokenとClaimsとRefresh Tokenが返ってきた。

```
Token:

eyJhbGciOiJSUzI1NiIsImtpZCI6ImIyYmQwYThmMjlhZTlhOGQzZmY4ZWNkY2RkZWJlZjA5MjEyYzZhNTAifQ.eyJpc3MiOiJodHRwOi8vMTI3LjAuMC4xOjU1NTYvZGV4Iiwic3ViIjoiQ2lRd09HRTROamcwWWkxa1lqZzRMVFJpTnpNdE9UQmhPUzB6WTJReE5qWXhaalUwTmpZU0JXeHZZMkZzIiwiYXVkIjoiZXhhbXBsZS1hcHAiLCJleHAiOjE1MDcwOTQ0NzksImlhdCI6MTUwNzAwODA3OSwiYXRfaGFzaCI6ImNQNEdEYkc0b3M5LVExd2ZEYnVzQ2ciLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsIm5hbWUiOiJhZG1pbiJ9.Kvtrz_i17jyeAXG9I1q_w1e2d_9zQHxRkY4EYhnD8-VVYVG0UPpOuwKvJ49HzRVE-o4lGwXRUbmkMf5Y_F10wMiIHeUOjYX9A9DwU0qEBtDnH4vJAQs7d1ZQ57Fiwx5wIYee16SvA4f5c980R1AHFjOnnnT8cG90Ab6ZY6pp2co1ogEeKjH0LP9QyP7lYWWwLYZNFoT94SvdazZ151I4_MjkQ6tiLkY3lZeDCOJ5tnHeB-MOV8NtjF1tJwqqJ5PpjB7hRynGSxtCSSQCCTywBfStRbg66Jm66afLxLRsHO9WRhYc46jIjKqq_OQOoljxfHwvcYaPkLynurVsq_8X9A
Claims:

{
  "iss": "http://127.0.0.1:5556/dex",
  "sub": "CiQwOGE4Njg0Yi1kYjg4LTRiNzMtOTBhOS0zY2QxNjYxZjU0NjYSBWxvY2Fs",
  "aud": "example-app",
  "exp": 1507094479,
  "iat": 1507008079,
  "at_hash": "cP4GDbG4os9-Q1wfDbusCg",
  "email": "admin@example.com",
  "email_verified": true,
  "name": "admin"
}
Refresh Token:

ChlmYXZrZDN1Z3lyZTJpeWVlajNod2hlc2h0EhlxZXV5Ymcydnl6Zmw3bWJ6aGxjdjVscW53
```

dexのv2なのになんでuser/passwordでログインできるんだろう？と思ったら
先ほど使った、 `examples/config-dev.yaml` に以下のような設定があって、mockでローカルDBで認証してたっぽい。

```yaml

connectors:
- type: mockCallback
  id: mock
  name: Example

# Let dex keep a list of passwords which can be used to login to dex.
enablePasswordDB: true
```
# 所感
* oauth2_proxy的な使い方にはよさそう
* OpenID Connect以外にもSAMLとかLDAPとかいろいろ連携した時に前段に挟んでおくと、OpenID ConnectとしてIDトークン発行できるので、APIサーバ側からは統一的に検証できてよさそう。
* ドキュメントがあんまりないので、分からないことあればコード読むしかない
* ログイン画面にCoreOSのロゴが出てるんだけど、コードみたかんじWebConfigって言うので差し替えできるように見えるものの、[カスタマイズするのはオススメしないというコードコメント](https://github.com/coreos/dex/blob/fe1516332c4a41aee1dcab66164660676527a36c/server/server.go#L76-L80)があったりして、社内用ならいいけど、エンドユーザに見せる画面作るつもりならいろいろ制約がありそうで厳しい印象。