Cloudflare Zero Trust の ID 認証先に Amazon Cognito を OIDC で連携する

# 目的
例えば、セルフホストアプリに対して認証をかけたいときに、Cloudflare に登録された公開ドメインを使って、以下のような構成を組むことができます。
Cloudflare Zero Trust では ID 認証先に OIDC や SAML 2.0 で複数登録し、アプリごとに使い分けたり、IP コンテキストやデバイスポスチャ等の他の要素を組み合わせて、アクセスポリシーを制御できます。
今回はこのようなユースケースを想定し、Cloudflare Zero Trust の ID 認証先に Amazon Cognito を OIDC で連携します。

```mermaid:
%%{init: {'themeCSS': 'img {width: 48px; height:48px;}'}}%%
graph LR

User[<img src='https://cf-icons.pages.dev/user.svg' width='48' height='48'/><br>User]
Auth{CF_Authorization<br>cookie ?}
ZT[<img src='https://cf-icons.pages.dev/cloudflare-zero-trust.svg' width='48' height='48'/><br>Cloudflare Zero Trust]
Cognito[<img src='https://d2ga7imph990gt.cloudfront.net/icon/7c8353710f9831898e50921c0879977a-393ba9c98bdcec2ebfd74a7cbac07d88.svg' width='48' height='48'><br>Amazon Cognito]
App[<img src='https://cf-icons.pages.dev/internet-browser.svg' width='48' height='48'/><br>Self-hosted App<br>secure.exmaple.jp]

User --> |Access|App
App --> |Evaluate|Auth --> |Yes<br>Allow|App
Auth --> |No<br>Redirect|ZT --> |Callback|App
ZT --> |Redirect|Cognito --> |Callback|ZT
Cognito --> |Signin|Cognito
ZT <-.-> |OIDC<br>integration|Cognito
```

# Amazon Cognito ユーザープール作成
以下の流れで Amazon Cognito ユーザープールを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/3f8cc829-9f95-45bd-baf6-53fea04e8ec0.png)

ユーザープールでの認証を使用します。

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/authentication.html

> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/bd8ca9f7-76a5-22be-d373-1186c96030e2.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/d8b2d803-0deb-8739-6a31-75a60c4b0ec9.png)

セキュリティ要件は適当に設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/26544d7d-5a1a-7a8f-defa-63b94db9124c.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/e664af73-d3d7-708e-010f-d5bbe4b6e517.png)

サインアップ体験は「Enable self-registration」を無効にして、管理者がユーザー作成する形式にします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/77afdea9-4fda-b5df-45b3-90799dde9d97.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/bd272ba4-3b6a-2e0f-50a9-c35bc64128a3.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/20cbd7a2-0a9e-200f-9ae0-3304693d44c4.png)

メール配信設定は「Send email with Cognito」にします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/bf0daa1f-5714-4562-6604-061f1deaba4e.png)

「Use the Cognito Hosted UI」をチェックします。
ドメインタイプは「Use a Cognito domain」を選択し、任意の「Cognito domain」を指定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/8f5df8a7-dde0-b8f9-009d-84651007859f.png)

アプリクライアント設定では「Confidential client」とし、「Generate a client secret」でシークレット生成します。
「Allowed callback URLs」は `https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback` を設定します。

:::note info

> - [What’s a team domain/team name? · Cloudflare Zero Trust docs](https://developers.cloudflare.com/cloudflare-one/faq/teams-getting-started-faq/#whats-a-team-domain/team-name)
> チームドメインは、Cloudflareアカウントに割り当てられた一意のサブドメインです。例えば、`<your-team-name>.cloudflareaccess.com` のようになります。チームドメインの設定は、Zero Trustの設定において不可欠なステップです。ここでユーザはCloudflare Zero Trustで保護されたアプリを見つけ（App Launcherに表示されます）、それらにログイン要求することができます。チームドメインのカスタマイズ可能な部分はチーム名と呼ばれます。チーム名とチームドメインは、Zero TrustのSettings（設定）> Custom Pages（カスタムページ）で確認できます。
> 
> | team name | team domain |
> |:-:|:-:|
> | `your-team-name` | `<your-team-name>.cloudflareaccess.com` |
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/b00dbc0d-531a-e8da-a033-6f15f77260c5.png)

「Advanced app client settings」で OpenID Connect scopes 設定を　`OpenID`、`Email`、`Profile` を設定します。
「Allowed sign-out URLs」は `https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/logout` を設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/cf2e88ca-b31b-2b3a-cc3c-2120fd12d894.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/614332ed-b3e3-527b-1912-eea7c3ac5c5c.png)

これで AWS Cognito ユーザープール作成は完了です。

# Cognito ユーザープールでユーザー作成
テストログインに使う適当なユーザーを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/d3cbed31-1a6c-997a-b42d-95e1fb1918c5.png)

# Cloudflare Zero Trust の ID 認証先に Amazon Cognito を OIDC で連携
以下のリファレンスと設定画面を参照し、必要な情報を集めます。

https://docs.aws.amazon.com/cognito/latest/developerguide/federation-endpoints.html

| Cloudflare Zero Trust <br>での設定項目 | 値 |
|:-:|:-:|
| App (Client) ID | 4u2loooooooooo |
| Client Secret | xxxxxxxxxxxxxxxx |
| Auth URL | `https://<Your user pool domain>/oauth2/authorize` |
| Token URL | `https://<Your user pool domain>/oauth2/token` |
| Certificate (key) URL | `https://cognito-idp.<Region>.amazonaws.com/<your user pool ID>/.well-known/jwks.json` |

生成された App (Client) ID と Client Secret は以下の画面から確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/dc3be79f-d7e1-9610-278d-345ae1aa226a.png)

Certificate (key) URL は以下の画面からも確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/8c4769e0-a997-5366-162b-881516c79a88.png)

集めた情報を Cloudflare Zero Trust で設定します。

`Settings > Authentication > Login methods >Add new > OpenID Connect` から Amazon Cognito を OIDC 連携先として追加します。

https://developers.cloudflare.com/cloudflare-one/identity/idp-integration/generic-oidc/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/fa2a8753-0641-be34-b305-b76671b7fb8e.png)

# 接続テスト
以下の画面から「Test」をクリックすると、ログインフローをテストできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/64be2b92-53ff-8506-d6ab-63719757f9b7.png)

ユーザープール内に作成したユーザーの認証情報を使ってサインインします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/fb6aa126-598f-ae1e-191c-491ff04e9337.png)

管理者でユーザー作成したため、初回ログイン時はユーザー側でパスワード変更を求められます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/94e66393-f003-e81a-aef7-adc4a79a9888.png)

これで問題なく Cloudflare Zero Trust の ID 認証先に AWS Cognito を OIDC で連携できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/f9e4a820-c7f1-347d-3687-99a4bac5e9e5.png)

# Custom Claims を使ったユーザー属性情報の連携
上記の単純な連携設定だと ID トークン内の `email` 属性情報のみが連携されて、ポリシー評価に使用できる形です。
他にも以下の ID トークンに含まれる属性情報をフィールドを指定して引っ張ってくることで、Cloudflare Zero Trust でのポリシー評価に使用できます。

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-with-identity-providers.html

> - `sub`
>   - 認証されたユーザーの固有識別子 (UID) または件名。ユーザー名はユーザープール内で一意ではない可能性があります。sub クレームは、特定のユーザーを識別する最良の方法です。
> - `cognito:groups`
>   - ユーザーをメンバーとするユーザープールグループの名前の配列。グループは、アプリに提示する識別子として使用したり、アイデンティティプールへの優先 IAM ロールのリクエストの生成に使用したりできます。
> - `cognito:username`
>   - ユーザープール内のユーザーのユーザー名。

## ユーザーに標準属性を追加
例えば「Edit user > Add attribute」から標準属性 `name` を追加できます。

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/user-pool-settings-attributes.html#user-pool-settings-custom-attributes

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/41d10a92-6e90-2eba-d193-15c502d9d2f6.png)


## ユーザーをグループに追加
ユーザープールにグループを作成します。

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-user-pools-user-groups.html

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/9d9da3aa-d09b-df50-c64c-82e7328936d0.png)

その後、ユーザーをグループメンバーに追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/94ae7263-9361-9152-62d2-0c056f412007.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/f44359e4-51d2-9b81-aa3a-12f8b3c1bf80.png)

## Cloudflare Zero Trust で Custom Claims を設定
Cloudflare Zero Trust で先ほど OIDC 連携した AWS Cognito の認証先情報でオプション設定を追加します。

> OIDCクレーム
OIDC統合はカスタムOIDCクレームの使用をサポートします。カスタムOIDCクレームはアクセスポリシーで参照することができ、特定の属性に基づいてユーザーアクセスを制御する手段を提供します。カスタムOIDCクレームは、現在ゲートウェイポリシーではサポートされていません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/93e37340-7ec4-c883-11ae-bcd1e35e4d4f.png)

「Save」した後、「Test」します。
先ほどと比べて新しく `oidc_fields` として Custom Claims で指定した属性情報を取得できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/1f96f891-3bb5-ca7b-218d-21b7c1de813d.png)

この情報は Access ポリシーの OIDC Claim セレクタで使用できます。

https://developers.cloudflare.com/cloudflare-one/policies/access/#selectors

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/f5591140-d6cc-e361-8f91-28d090dcbf7b.png)

:::note info
2024年6月現在、カスタムOIDCクレームは、Gateway ポリシーではサポートされていません。
:::

https://developers.cloudflare.com/cloudflare-one/policies/gateway/identity-selectors/#generic-oidc-idp

> **Generic OIDC IdP**
Custom OIDC claims are not supported in Gateway policies.

# 【参考】セルフホストアプリへのアクセス
以下の手順で、Cloudflare に登録された公開ドメインを使ったセルフホストアプリに対して、Amazon Cognito を ID 認証先としたアクセスポリシー制御をかけることができます。

https://developers.cloudflare.com/cloudflare-one/applications/configure-apps/self-hosted-apps/

> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/cabb4611-e87e-9953-de26-2b3b1988dbe5.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/ff1db7e0-7256-776d-db48-d2fb07cced8a.png)

セルフホストアプリのオリジンには、以下のような Cookie やトークン情報がヘッダとして送られ、トークン検証等に活用することができます。

https://developers.cloudflare.com/cloudflare-one/identity/authorization-cookie/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/5e5bce6f-8fda-38e5-4d04-cc98ea698f34.png)


# まとめ

手軽にユーザープールを作って、外部向けサイトに認証機構をかける組み合わせとしては、よく使われる可能性があるかなと思います。

外部 ID プールも連携できるため、様々な連携方式が考えられるとともに、Cloudflare Zero Trust の機能も活用できると幅広い ID コンテキストに対してゼロトラストなセキュリティを構成できるため、これだけ使いやすい Cloudflare もおすすめです。

# 参考

- [[AWS Black Belt Online Seminar] Amazon Cognito](https://d1.awsstatic.com/webinars/jp/pdf/services/20200630_AWS_BlackBelt_Amazon%20Cognito.pdf)