nginx でホストしたサイトに Cognito で OAuth2 認証をかける

はじめに
----------------
Sphinx などで生成したフラットファイルを公開するのに、認証をかけたい場合、Basic認証などもありますが、公開先のユーザーが削除されたりするとパスワード変更や通知の手間が大変なので、OAuth2 認証をかけたい、といった状況があると思います。

そういった場合に、[oauth2_proxy](https://github.com/oauth2-proxy/oauth2-proxy)と nginx が使えることが、こちらのとても素晴らしいサイト[Ytaka Kato oauth2_proxy と Auth0 を用いた Nginx のお手軽 OAuth 化](https://mikan.github.io/2018/05/23/enable-oauth-to-your-nginx-by-oauth2-proxy-and-auth0/)に紹介されています。

今回はこちらのサイトに紹介されている内容を、AWS Cognito を使って実施してみました。（Cognito部分以外は前述のページを参照いただいた方が良いかもしれません。。）備忘録としてのメモです。

サイトのSSL化
-----------------------
Cognito で認証をかけるには、コールバックURLが https である必要があります。まず nginx でホストしている、認証をかけたいサイトをSSL化する必要があります。

[acme-tiny](acme-tiny)などを使って、Let's Encrypt でサーバ証明書を取得し、Virtual Host を SSL化します。

```
server {
    listen 443 ssl;
    server_name your.domain.name;

    ssl_certificate /path/to/signed.crt;
    ssl_certificate_key /path/to/domain.key;
    ssl_session_timeout 5m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA;
    ssl_session_cache shared:SSL:50m;
    ssl_dhparam /path/to/server.dhparam;
    ssl_prefer_server_ciphers on;

    location / {
        index index.html;
        try_files $uri $uri/ =404;
        alias /var/www/somedocument/;
    }

}
```

oauth2_proxy のインストール
---------------------------

[こちら](https://oauth2-proxy.github.io/oauth2-proxy/installation) を参考にして、バイナリをダウンロードします。go製なのでセットアップが簡単でいいですね。
今回は nginx と同じマシンにインストールしました。設定は後から行います。

systemdでサービス化するサンプルも、同じプロジェクトの [contrib](https://github.com/oauth2-proxy/oauth2-proxy/blob/master/contrib/oauth2-proxy.service.example)で公開されています。

Cognito でユーザープールを作る
------------------------------

Cognito のコンソールにログインします。

### 「ユーザープールの管理」をクリックします
<img width="1283" alt="cognito-00.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/564576/224568e6-5ca7-e8f6-5e6b-a337666f7099.jpeg">


### 「ユーザープールの作成」をクリックします
<img width="1283" alt="cognito-01.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/564576/a1a35bd0-225d-caa3-b4d3-592e1ac10e2e.jpeg">


### 「属性」部分は用途に応じて適当に設定します
![cognito-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/564576/3e94883e-c88c-fa2a-fec3-3778d5f31b33.png)


### 「ポリシー」でパスワード強度等を設定します
![cognito-03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/564576/b59330f1-2ecb-e0da-3d48-79cb4fcc80da.png)

### 「MFAそして確認」では多要素認証について設定します
![cognito-04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/564576/a76b3fa2-ab9d-553c-c890-db85273397c4.png)

今回はMFAを無効にします。

### 「アプリクライアント」でアプリケーションを追加します
![cognito-05.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/564576/e4591df2-f042-1ee5-2f71-0ab2a2e8f38d.jpeg)

ここではトークンの有効期限を設定できます。

### アプリの統合 / アプリクライアントの設定
![cognito-06.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/564576/dacf038a-a8f6-3ad6-4e80-340e8a116093.jpeg)

「アプリの統合 / アプリクライアントの設定」で、コールバックURL、サインアウトURLを設定します。
* コールバックURLは、oauth2_proxy をホストしているURLを設定します。URLのパスは、```/oauth2/callback``` です。
* コールバックURLは、https である必要があります。
* 「有効なIDプロバイダ」で、「Cognito User Pool」にチェックを入れます

### アプリの統合 / ドメイン名
![cognito-07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/564576/ced58e9f-b03f-fe35-99d9-f7a0b1d1dd43.png)

「アプリの統合 / ドメイン名」で、適当な値を設定します。


Oauth2_proxy を Cognito 向けに構成する
---------------------------------------------
https://oauth2-proxy.github.io/oauth2-proxy/auth-configuration#openid-connect-provider

こちらのチケット[Integrating oauth2_proxy with AWS Cognito](https://github.com/bitly/oauth2_proxy/issues/674)にサンプルの構成例が出ています。
こちらを参考に、下記のような構成で動作しました。

細かい設定パラメーターについては、[OAuth2 Proxy の Configurationページ](https://oauth2-proxy.github.io/oauth2-proxy/configuration)に書かれています。


```
provider = "oidc"
client_id = "XXXXXXXXXXXXXXXXXXX"
client_secret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
oidc-issuer-url = "https://cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-1_XXXXXXXXX" 
redirect_url = "https://my.oauth2proxy.domain/oauth2/callback"
login-url = "https://tsh-rpa-support-100.auth.ap-northeast-1.amazoncognito.com/oauth2/authorize"
profile-rul =  "https://tsh-rpa-support-100.auth.ap-northeast-1.amazoncognito.com/oauth2/userInfo"
redeem-url = "https://tsh-rpa-support-100.auth.ap-northeast-1.amazoncognito.com/oauth2/token"
scope = "openid"
cookie_secure = false
#upstream = "URL OF THE APP"
email_domains = [
    "*"
]
cookie_secret = "secret"
http_address = "127.0.0.1:4180"
whitelist-domain = "tsh-rpa-support-100.auth.ap-northeast-1.amazoncognito.com"
```



### oidc-issuer-url について
[Cognito の Developer Guide](https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-with-identity-providers.html#amazon-cognito-user-pools-using-the-id-token)に、```Issuer (iss)```のフォーマットが書かれています。

> Issuer (iss)
> The iss claim has the following format:
> https://cognito-idp.{region}.amazonaws.com/{userPoolId}.
> For example, if you created a user pool in the us-east-1 region and its user pool ID is u123456, the ID token issued for users of your user pool have an iss claim value of
> https://cognito-idp.us-east-1.amazonaws.com/u123456.

User Pool ID は、ユーザープールの「全般設定」で確認できます。

<img width="1228" alt="pool_id.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/564576/1397961b-d054-19a1-9320-6805f2ae6e40.jpeg">


※ 手元で試した環境では、設定ファイルの```oidc-issuer-url```が効かず、起動パラメーターとして指定しました。
```
/opt/oauth2_proxy/oauth2_proxy -config /etc/oauth2proxy.conf --oidc-issuer-url="https://cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-1_XXXXXXXXXXX
```


### login-url, profile-rul, redeem-url の URL
このドメインは、「アプリの統合 / ドメイン名」で指定したものを使います。

### redirect_url
「アプリの統合 / アプリクライアントの設定」で指定したコールバックURLと同じ物です。
（Oauth2_Proxy をホストしているURLを指定します。URLのパスは、```/oauth2/callback``` です）


サイトに OAuth2 認証を設定する
-------------------------------------

先ほど SSL化した Virtual Host に対して、OAuth2 認証を設定します。

nginx の auth_request を使うところがポイントのようです。

```
server {
    listen 443 ssl;
    server_name your.domain.name;

    ssl_certificate /path/to/signed.crt;
    ssl_certificate_key /path/to/domain.key;
    ssl_session_timeout 5m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA;
    ssl_session_cache shared:SSL:50m;
    ssl_dhparam /path/to/server.dhparam;
    ssl_prefer_server_ciphers on;

    location / {
        index index.html;
        try_files $uri $uri/ =404;
        alias /var/www/somedocument/;

        auth_request /oauth2/auth;
        error_page 401 = /oauth2/sign_in;
    }


    location /oauth2/ {
        proxy_pass       http://127.0.0.1:4180;
        proxy_set_header Host                    $host;
        proxy_set_header X-Real-IP               $remote_addr;
        proxy_set_header X-Scheme                $scheme;
        proxy_set_header X-Auth-Request-Redirect $request_uri;
        # or, if you are handling multiple domains:
        # proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri;
    }
    location = /oauth2/auth {
        proxy_pass       http://127.0.0.1:4180;
        proxy_set_header Host             $host;
        proxy_set_header X-Real-IP        $remote_addr;
        proxy_set_header X-Scheme         $scheme;
        # nginx auth_request includes headers but not body
        proxy_set_header Content-Length   "";
        proxy_pass_request_body           off;
    }


}
```

試してみる
-----------------

### 認証をかけたサイトを開く
<img width="1064" alt="Sign_In.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/564576/4b2cfaae-613c-74d3-57e6-bf5191132316.jpeg">

ログインのためのボタンが表示されます。クリックします。

### ログイン画面で認証情報を入力する
<img width="804" alt="login.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/564576/a21a43d8-3889-6f1c-c001-f4af4987af52.jpeg">

Cognito のログイン画面が表示されるので、認証情報を入力します。

ログインが成功すると、認証をかけたサイトの内容が表示されます。