Cloudflare Accessでアプリケーションの追加実装なくSSOを実現する

Cloudflare AccessとArgo Tunnelを使うとアプリケーション側への追加実装なしでシングルサインオンを実現できます。

[Cloudflare Access](https://www.cloudflare.com/ja-jp/teams/access/)を使うとCloudflareのプロキシを使ってOAuthなどによる認証ができます。[Argo Tunnel](https://www.cloudflare.com/ja-jp/products/tunnel/)はサービスをCloudflareを通じて安全に公開するためのトンネリングサービスです。

##### ポイント

 - 認証はCloudflareのプロキシで行われるためアプリケーションは追加の実装が不要。
 - アプリケーションサービスはグローバルIPを持たなくて良い。

## 前提

 - Cloudflareアカウントを取得済みであること。
 - Cloudflareにドメインを登録し初期設定してあること。

## cloudflaredクライアントのインストール

Argo Tunnel関連の操作をするためのクライアントをインストールします。cloudflaredを使ってトンネルの作成やトンネルサービスを起動を行います。

```bash
brew install cloudflare/cloudflare/cloudflared
```

## Cloudflareの認証

コマンドを打つとブラウザが開くのでCloudflareのアカウントで認証します。

```bash
cloudflared login
```

## トンネルの作成

下記のコマンドを打つとトンネルのID(UUID)が払い出されます。
また、$HOME/.cloudflared にJSONファイルが出力されます。

```bash
cloudflared tunnel create secure-site
```

## 設定ファイルの作成

設定ファイル config.yaml を作成します。[Ingress rules](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/configuration-file/ingress)を使ってホストやパスによって接続先のサービスを分けることができます。

ここではフロントアプリケーションとバックエンドアプリケーションでポートが分かれていることを想定した設定とします。

```yaml
tunnel: <払い出されたUUID>
credentials-file: <出力されたJSONファイルのフルパス>

# ルーティングの設定
ingress:
  - service: http://127.0.0.1:8081
    path: /api/*
  - service: http://127.0.0.1:8080
```

## 動作確認用のサーバー起動

動作確認用に上記の設定に対応するサーバーを起動しておきます。

```bash
mkdir 8080
echo "<body>8080 server</body>" > 8080/index.html
mkdir -p 8081/api
echo "<body>8081 server</body>" > 8081/api/index.html

# 下記二つのコマンドは別々のコンソールで実行
PORT=8080 npx serve 8080
PORT=8081 npx serve 8081
```

## トンネルのサービスを起動

```bash
cloudflared tunnel --config config.yaml run
```

## DNSの設定

Cloudflareの管理画面からDNSレコードを登録します。

レコードタイプ: CNAME
名前(サブドメイン名): 任意
ターゲット: <払い出されたUUID>.cfargotunnel.com
プロキシ有効

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6571/f34e4ec3-0e4c-236c-13ea-3a4db88da3a9.png)

ここまでの手順で指定したドメインを使ってトンネルを介してサービスへ接続できるようになります。
シングルサインオンの設定をする前に一度動作確認をしてください。

 - [公式のDNS関連のリファレンス](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/routing-to-tunnel/dns#route-traffic-from-the-cloudflare-dashboard)

## シングルサインオン(SSO)の設定

1. Cloudflareのコンソール画面から「Access」を選択します。
2. ログインメソッドの追加をクリックしてIDプロバイダーを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6571/2597930b-c141-a707-89e1-be85a5b24cfc.png)
3. Googleの場合は下記のような画面になります。IDプロバイダーごとのクライアントIDとクライアントシークレットを入力して保存します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6571/2feab592-c571-caa3-ea15-f76413423467.png)
4. AccessポリシーのパネルからAccessポリシーを作成します。画面でサブドメインやポリシーを設定して保存します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6571/25882e3a-54f6-6288-a25e-89276f36f12a.png)


これでSSOが有効になります。ブラウザでサイトにアクセスすると下記のような認証画面が表示され、認証に成功するとサイトが表示できるようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6571/41b66cd6-23e3-0d71-da13-0755ae2e87fb.png)


## 補足

 - クッキーを使ってドメイン単位で認証されるため、ドメインが同じであればフロントアプリケーションとバックエンドアプリケーションの両方の認証を一度に行うことができる。
 - WebSocketの接続もできる。
 - アプリケーションは[JWTによって認証ユーザーを識別することもできる](https://developers.cloudflare.com/cloudflare-one/identity/users/validating-json#programmatic-verification)。
 - HTTPだけでなくTCP、SSH、RDPなどのトンネルが作成できる。[参考](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/configuration-file/ingress#supported-protocols)

## 参考

 - [公式ドキュメント](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/configuration-file)