AWS Amplify React アプリで Azure AD のカスタムドメインと SSO してみた

## はじめに

エンタープライズ向けに AWS Amplify を採用すると、Azure AD や Octa のような Identity Provider（IdP）とのシングルサインオン要件が必ずと言っていいほど挙がります。Azure AD と Amazon Cognito の公式ドキュメントや様々なブログに手順はまとまっていますが、いざ実装しようとすると手順が多く、結構手間が掛かります。せっかくの機会ですので、キャプチャ多めで手順を丁寧にまとめておきたいと思います。皆さんのお役に立てば幸いです。

### ソーシャルサインインの場合

Amplify UI の Authenticator コンポーネントを利用することで、ソーシャルサインイン（Amazon, Apple, Facebook, Google）には ``socialProviders`` パラメータ指定だけで対応可能です。これはありがたいですね、、、。しかし、SAML の場合は、そうもいきません。

https://ui.docs.amplify.aws/components/authenticator?platform=react#socialproviders

```JSX:ソーシャルサインインはパラメータ指定のみ！
import { Authenticator } from "@aws-amplify/ui-react";
import "@aws-amplify/ui-react/styles.css";

function App() {
  return (
    <Authenticator socialProviders={["amazon", "apple", "facebook", "google"]}>
      {({ signOut, user }) => (
        <main>
          <h1>Hello {user.username}</h1>
          <button onClick={signOut}>Sign out</button>
        </main>
      )}
    </Authenticator>
  );
}

export default App;
```
Amplify UI の Authenticator コンポーネントは、以下のように表示されます。現在のバージョンは、ヘッダやフッタを ``component`` パラメータで指定することで拡張可能となり、だいぶ使い勝手が良くなりました。例えば、ヘッダに独自の「Sign In with Azure AD」ボタンを追加し、``Auth.federatedSignIn();`` を呼び出すことも可能です。しかし、Amplify サインイン、Cognito サインイン、Microsoft サインインと 3 つのサインイン UI を経由するのは、少々手間です。そもそも、Amazon/Apple/Facebook/Google or Cognito という選択肢を与えたくありません。エンタープライズアプリケーションのため Azure AD 一択で良いのです。
![screenshot-localhost_3001-2022.02.23-16_12_09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/55b61e0d-aba9-d43f-153c-c8a45c180f30.png)

### やりたいこと

やりたいことは至ってシンプルで、**「Azure AD との SSO でのみサインイン可能な Amplify アプリ」** の実現です。また先述したように、可能な限り **「いきなり SSO」** に近づけることです。
![what-I-want-to-do.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/c99eb8f6-ac77-0ef7-2b06-2838186a9636.png)

本記事は、以下の記事の続編となります。やりたいことの骨子は同じなので、大変参考になりました。ただ、Amazon Cognito の画面レイアウトが New Interface に刷新され、ガラッと変わってしまったので、対象項目を探すのが大変でした、、、。その意味でも、丁寧にキャプチャを取得しました。少なくとも New Interface が維持されるであろう数年間は、役に立つと思います。

- [Azure ADをAWS Cognito Federated IdPに追加して、Amplify+Vueアプリでログインする（模索中）](https://qiita.com/ksukenobe/items/a80e0e4435bcddd5e379)

### 前提条件

環境まわりをまとめておきます。

|ソフトウェアおよびライブラリ|バージョン|
|----|----|
|node|16.0.0|
|yarn|1.22.11|
|@aws-amplify/cli|7.6.15|
|react|^17.0.2|
|typescript|^4.4.2|
|aws-amplify|^4.3.14|
|@aws-amplify/ui-react|^2.5.0|

## SSO したい Amplify アプリを用意する

まず、最終的に Azure AD と SSO したい AWS Amplify アプリを用意します。手順は以下の通りです。

1. React アプリを作成する
1. Amplify ライブラリを追加する
1. AWS Amplify を初期化する（適用する）
1. Amplify ホスティングを追加する
1. React アプリの起動確認を行う
1. Amazon Cognito 認証を追加する

### React アプリを作成する

アプリ名 ``sso-with-aad`` で React アプリを作成します（適宜読み替えてください）。TypeScript にしておきます。

```zsh
% yarn create react-app sso-with-aad --template typescript
```

### Amplify ライブラリを追加する

プロジェクトフォルダに移動し、``aws-amplify``、``@aws-amplify/ui-react`` を追加しておきます。

```zsh
% cd sso-with-aad
% yarn add aws-amplify @aws-amplify/ui-react 
```

### AWS Amplify を初期化する（適用する）

``amplify init`` コマンドを実行します。インタラクティブなパラメータ指定は、すべてデフォルトのままです。

```zsh
% amplify init
```

### Amplify ホスティングを追加する

``amplify add hosting`` コマンドで Amplify ホスティングを追加します。単なる検証ですので、``Manual deployment`` を選択します（``CI/CD`` を設定すると、``git push`` トリガーで自動パブリッシュされるようになります）。次に ``amplify publish`` コマンドでクラウド環境に手動デプロイします。CloudFormation が実行され、3分ほど待つと Cognito ユーザプールが作成されます。CloudFormation が実行され、1分もかからずに Web アプリがデプロイされます。

|項目|入力値|
|----|----|
|Select the plugin module to execute Hosting with Amplify Console (Managed hosting with custom domains, Continuous deployment)|Manual deployment|

```zsh
% amplify add hosting
% amplify publish    
```

### React アプリの起動確認を行う

React テンプレートのままですが、念のため、起動確認しておきましょう。URL は ``amplify publish`` コマンドのコンソール出力の最後の行に表示されています。後続の手順で必要となるため、URL を控えておきます。アクセスすると、毎度お馴染みの React テンプレートアプリが正常に起動されました。まだ、フロントエンド側に一切の認証設定をしていないので、サインイン不要のシンプルなアプリとなっています。「とりあえずローカルで」という方は、``yarn start`` して、http://localhost:3000 にアクセスしてください。
![screenshot-localhost_3000-2022.02.13-18_18_41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/3ace4899-7980-e5fb-5ad6-41197d7c63d4.png)

### Amazon Cognito 認証を追加する

``amplify add auth`` コマンドで認証を追加します。数が多いですが、以下のパラメータを指定します。最初に ``Manual configuration`` を選択することで、OAuth フローの追加設定が可能になります。次に ``amplify push`` コマンドでクラウド環境にデプロイします。CloudFormation が実行され、3分ほど待つと Cognito ユーザプールが作成されます。

|項目|入力値|
|----|----|
|Do you want to use the default authentication and security configuration?|Manual configuration|
|Select the authentication/authorization services that you want to use|User Sign-Up & Sign-In only (Best used with a cloud API only)|
|Provide a friendly name for your resource that will be used to label this category in the project|\<提案値のまま\>|
|Provide a name for your user pool|\<提案値のまま\>|
|How do you want users to be able to sign in?|Email|
|Do you want to add User Pool Groups?|No|
|Do you want to add an admin queries API?|No|
|Multifactor authentication (MFA) user login options|OFF|
|Email based user registration/forgot password|Enabled (Requires per-user email entry at registration)|
|Specify an email verification subject|Your verification code|
|Specify an email verification message|Your verification code is {####}|
|Do you want to override the default password policy for this User Pool?|No|
|What attributes are required for signing up?|Email|
|Specify the app's refresh token expiration period (in days)|30|
|Do you want to specify the user attributes this app can read and write?|No|
|Do you want to enable any of the following capabilities?|（指定しない）|
|Do you want to use an OAuth flow?|Yes|
|What domain name prefix do you want to use?|\<提案値のまま\>|
|Enter your redirect signin URI|\<Amplify アプリのURL（最後は"/"）\>|
|Enter your redirect signout URI|\<Amplify アプリ のURL（最後は"/"）\>|
|Select the OAuth flows enabled for this project|Authorization code grant|
|Select the OAuth scopes enabled for this project|Phone, Email, OpenID, Profile, aws.cognito.signin.user.admin|
|Select the social providers you want to configure for your user pool|（何も選択しない）|
|Do you want to configure Lambda Triggers for Cognito?|No|

```zsh
% amplify add auth
% amplify push
```

## Azure AD に独自ドメインを追加する

元々、検証用に Route 53 でドメインを取得済みでしたので、今回はそちらのドメインを利用します。仮に、``mydomain.com`` とします。

1. カスタムドメインを追加する
1. TXT レコードを追加する（Amazon Route 53）
1. 追加したカスタムドメインを検証する
1. カスタムドメインをプライマリドメインに変更する

### カスタムドメインを追加する

Azure Services の中から「Azure Active Directory」を選択します。
![screenshot-portal.azure.com-2022.02.14-06_51_22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/5bfe3b05-063d-21cd-11bd-f16b8873e9fa.png)

サイドメニューの「Custom domain names」を選択すると、デフォルトドメインである ``<Your Domain>outlook.onmicrosoft.com`` が表示されます。ツールバーの「Add custom domain」を選択し、画面左のポップアップの「Custom domain name」フィールドに独自ドメイン名を入力し、「Add domain」ボタンを選択します。
![screenshot-portal.azure.com-2022.02.13-18_33_55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/5b8a63e6-eeed-ec2d-eed5-0e601edf6006.png)

カスタムドメインが追加され、検証のための手順が指示されます。「Verify」ボタンを選択する前に、以下の TXT レコードを DNS に登録する必要があります。次の手順で、Route 53 の TXT レコードを編集します。

|キー|値|
|----|----|
|Record type|TXT|
|Alias or host name|@|
|Destinationor points to address|MS=\<ms から始まる一意な値\>|
|TTL|3600|


![screenshot-portal.azure.com-2022.02.13-18_34_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/fbbff87c-ee2a-d092-52ec-46841da7b1b4.png)

### TXT レコードを追加する（Amazon Route 53）

DNS に Route 53 を利用しているので、AWS マネジメントコンソールから Route 53 を開き、対象の Hosted Zone（仮に ``mydomain.com``）を選択します。既に Alias or host name（Route 53 画面上は「Record name」フィールド）が空の TXT レコードがありましたので、「Value」に ``MS`` キーを追加しました。TTL は、指示された ``3600`` とは異なりますが、元の ``300`` のままとします。

|キー|値|
|----|----|
|Record name|（@ ではなく空で OK）|
|Record type|TXT|
|Value|（二行目にダブルクォーテーションで囲んだ）"MS=\<ms から始まる一意な値\>"|
|TTL|（指示された3600と違いますが既存レコードのまま）300|
![screenshot-console.aws.amazon.com-2022.02.14-09_28_58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/eb6bc90b-3e0e-2947-5f00-76056f24b89a.png)

### 追加したカスタムドメインを検証する

TXT レコードを変更（あるいは追加）して即座に「Verify」ボタンを選択すると、以下のエラーメッセージが表示されて、検証に失敗します。

> Could not find the DNS record for this domain.DNS changes may take up to 72 hours to propagate. Please try again later.

![screenshot-portal.azure.com-2022.02.13-18_35_54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/ce6ac5f3-0b9c-aec8-719f-e48caafc580b.png)

5分ほど伝播するのを待ってからリトライすると、以下の通り検証に成功しました。
![screenshot-portal.azure.com-2022.02.13-18_38_45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/f3bd50ab-b5d4-0ce6-cd9c-018e41f342d9.png)

### カスタムドメインをプライマリドメインに変更する

続けて、カスタムドメイン（仮に ``mydomain.com``）をプライマリドメインに変更しておきます。検証成功直後の画面のツールバーから　「Make primary」を選択します。確認ダイアログに対して、Yes 応答します。
![screenshot-portal.azure.com-2022.02.13-18_39_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/f9111f5d-3f8a-1309-4bd0-c17d751d75d2.png)

以上の手順で、独自ドメイン（仮に ``mydomain.com``）が Azure AD に検証済みのプライマリドメインとして登録されました。
![screenshot-portal.azure.com-2022.02.13-18_39_28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/8d9c9907-c02b-2498-2a01-a29da5f7ca8a.png)

## Azure AD に検証用ユーザを追加する

独自ドメイン（仮に ``mydomain.com``）に検証用ユーザを追加します。アプリケーションのサインイン時に指定するユーザです。

1. 検証用ユーザを追加する
1. メールアドレスを追加する

### 検証用ユーザを追加する

Azure Active Directory のデフォルトディレクトリ（仮に ``mydomain.com``）を選択し、サイドメニューの「Users」を選択します。
![screenshot-portal.azure.com-2022.02.13-18_31_43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/b89b70cd-f976-5835-920c-f80461eb0bbf.png)

現在、Azure 管理者の Microsoft アカウントのみ登録されています。ツールバーの「New user」を選択し、検証用ユーザを追加していきます。
![screenshot-portal.azure.com-2022.02.14-10_56_00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/001b785d-1244-6d27-7468-403c298e2b4b.png)

「Create user」をチェックし、以下の項目を入力して 「Create」ボタンを選択します。

|キー|値|
|----|----|
|User name|\<myalias>@\<mydomain.com\>|
|Name|フルネーム|

Identity セクションの下に、Password/Groups and roles/Settings/Job info セクションが続きますが、特に変更しません。
![screenshot-portal.azure.com-2022.02.14-10_57_11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/ff77e78d-e16d-0876-8836-946d6a5d8a07.png)

以上の手順で、検証用ユーザが作成されました。続けて、メールアドレスを追加していきます。
![screenshot-portal.azure.com-2022.02.14-11_04_41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/19c8588d-8af9-1f4c-dd6a-19cdd072b233.png)

### メールアドレスを追加する

後ほど必要になるので、検証用ユーザのメールアドレスを設定しておきます。ユーザ一覧から検証用ユーザを選択し、ツールバーの「Edit」を選択します。
![screenshot-portal.azure.com-2022.02.14-11_04_58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/88ccb57c-1a22-6871-189a-6eac8ac8595f.png)

Contact info セクションにある Email フィールドに連絡可能なメールアドレスを入力し、ツールバーの「Save」ボタンを選択します。
![screenshot-portal.azure.com-2022.02.14-11_05_43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/d795aebd-9ab9-d33d-edc8-a8ff3a021f54.png)

以上で、検証用ユーザに連絡可能なメールアドレスが設定されました。
![screenshot-portal.azure.com-2022.02.14-11_05_59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/3fa1c70a-12b6-1a95-a59d-98fec9ac443b.png)

## Amplify アプリを Azure AD エンタープライズアプリケーションとして設定する

最初の手順で作成した検証用 Amplify アプリを、Azure AD のエンタープライズアプリケーションとして設定します。

1. エンタープライズアプリケーションを追加する
1. エンタープライズアプリケーションにユーザを追加する
1. エンタープライズアプリケーションに Single sign-on (SSO) を設定する
1. エンタープライズアプリケーションの Single sign-on (SSO) をテストする

### エンタープライズアプリケーションを追加する

Azure Active Directory のデフォルトディレクトリ（仮に ``mydomain.com``）を選択し、サイドメニューの「Enterprise applications」を選択します。
![screenshot-portal.azure.com-2022.02.13-18_31_43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/00ee4cfe-207e-1b1b-84b9-d6f60de23f81.png)

ツールバーの「New application」を選択します。
![screenshot-portal.azure.com-2022.02.14-10_47_59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/54271b43-c9e6-8c34-cde2-3a4f99771af8.png)

ツールバーの「Create your own application」を選択します。
![screenshot-portal.azure.com-2022.02.14-10_48_37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/6be4d36d-de3e-65f0-de20-abe6b1f13b44.png)

アプリケーション名とアプリケーションタイプを入力・選択し、「Create」ボタンを選択します。アプリケーション名は少し長いですが、``AWS Amplify React (SSO with Azure AD)`` としました。

|キー|値|
|----|----|
|What's the name of your app?|\<アプリケーション名\>|
|What are you looking to do with your application?|Integrate any other application you don't fine in the gallery (Non-gallery)|

![screenshot-portal.azure.com-2022.02.14-10_51_45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/b95ccbed-b6b3-edf1-b7a2-2b67938d2e47.png)

以上の手順で、エンタープライズアプリケーション（``AWS Amplify React (SSO with Azure AD)``）が登録されました。
![screenshot-portal.azure.com-2022.02.14-10_54_20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/51f177c4-e4c3-96df-489b-5eb2018c12ef.png)

### エンタープライズアプリケーションにユーザを追加する

サイドメニューから「Users and groups」を選択し、ツールバーの「Add user/group」をクリックをします。
![screenshot-portal.azure.com-2022.02.14-10_55_08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/aeca83ba-8244-8361-13cc-5e8dd3d14340.png)

Users の ``None selected`` を選択します。
![screenshot-portal.azure.com-2022.02.14-12_40_45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/6f3647bb-1aa9-4254-e983-31bf33323f34.png)

追加したいユーザを選択し、「Select」ボタンを選択します。
![screenshot-portal.azure.com-2022.02.14-12_40_59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/8840ed4d-aee3-627e-aaa0-97a5f0925f9a.png)

``1 user selected.`` と表示されていることを確認し、「Assign」ボタンを選択します。
![screenshot-portal.azure.com-2022.02.14-12_41_08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/e7817dcf-1478-b2d0-a87d-cfb2d6ede004.png)

以上の手順で、AWS Amplify React (SSO with Azure AD) エンタープライズアプリケーションにユーザが割り当たりました。
![screenshot-portal.azure.com-2022.02.14-12_41_22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/3c1d0469-fb40-e9c9-734b-eed1dab23b35.png)

### エンタープライズアプリケーションに Single sign-on (SSO) を設定する

サイドメニューから「Single sign-on」を選択します。4 つの方式から「SAML」を選択します。
![screenshot-portal.azure.com-2022.02.14-12_43_25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/9e861b66-b577-e90a-bb04-02087c305c58.png)

Single Sign-On with SAML 設定画面が表示されます。ステップ 1：Basic SAML Configuration の「Edit」を選択します。
![screenshot-portal.azure.com-2022.02.14-12_43_36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/a43bf7e6-e140-95a0-b29b-d1ff68ea73de.png)

Identifier と Reply URL を入力し、「Save」ボタンを選択します。Cognito の User Pool ID と Cognito Domain （Hosted UI および Auth 2.0 エンドポイント）は、前の手順で控えておいたものを指定します。

|項目|入力値|
|----|----|
|Identifier (Entity ID)|urn:amazon:cognito:sp:\<Cognito User Pool ID\>|
|Reply URL (Assertion Consumer Service URL)|https://\<Cognito Domain\>.auth.ap-northeast-1.amazoncognito.com/saml2/idpresponse|
![screenshot-portal.azure.com-2022.02.14-12_53_49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/af7feab6-e065-8277-5571-2bd96782e6bf.png)

ステップ 3：SAML Signing Certificate の App Federation Metadata Url の値をコピーしておきます。後ほど、Cognito 側で設定します。
![screenshot-portal.azure.com-2022.02.14-13_11_19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/8aa50143-0699-8127-5fd3-747b710a11aa.png)

### エンタープライズアプリケーションの Single sign-on (SSO) をテストする

ステップ 5：Test single sign-on with AWS Amplify React (SSO with Azure AD) の「Test」ボタンを選択します。
![screenshot-portal.azure.com-2022.02.18-09_28_19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/14338aed-8708-bca8-a2d2-d04144aed3d2.png)

次の手順のために、Microsoft 謹製の My Apps Secure Sign-in Extension ブラウザ拡張をインストールしておきます（「Sign in as current user」を選択する場合は不要です）。今回は、Azure 管理者の Microsoft アカウントでサインインした状態で、検証用ユーザのサインインをテストするので、「Sign in as someone else (required browser extension)」を選択します。

![Screen Shot 2022-02-14 at 12.58.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/32681436-bf3d-4af5-5e36-824fe177d120.png)

「Sign in as someone else (required browser extension)」を選択し、「Test sign in」ボタンを選択します。
![screenshot-portal.azure.com-2022.02.18-09_28_39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/dee58942-757f-534e-17bb-ec2b3dfc7d4f.png)

先ほど追加した検証用ユーザの Azure AD アカウント（連絡用メールアドレスではなく、``myalias@mydomain.com`` の方です）を入力し、「Next」を選択します。
![screenshot-login.microsoftonline.com-2022.02.18-10_04_20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/6c295a12-ac7a-6540-7fcc-9d6c5ea53efe.png)

パスワードを入力し、「Sign in」を選択します。
![screenshot-login.microsoftonline.com-2022.02.18-10_04_08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/c495b65b-b15e-f673-7ea8-e29f03e6a73b.png)

Azure ADでの認証が OK となり、Reply URL で指定した Cognito エンドポイントにリダイレクトされ、別タブが開きます。パラメータエラーが発生していますが、現時点では無視して構いません。
![screenshot-ssowithaad-dev.auth.ap-northeast-1.amazoncognito.com-2022.02.18-10_05_36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/faa68143-1635-6e48-3684-524fc23bba50.png)

SSO テストに成功し、以下のメッセージが表示されました。右上の「×」で Test を閉じます。以上の手順で、AWS Amplify React (SSO with Azure AD) エンタープライズアプリケーションの設定が完了しました。続けて、リダイレクト先である Cognito の設定を行います。

> Azure AD successfully issued a token (SAML response) to the application (service provider). If you still can’t access the application you need to contact the software vendor and share the information below.

![screenshot-portal.azure.com-2022.02.18-10_05_21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/10e1e5c8-d30c-3d28-5619-ec3aeeb118fd.png)

## Amazon Cognito を設定する

いよいよ設定の大詰めです。まず、Cognito から見たときの Federated IdP として Azure AD を追加し、

1. アイデンティティプロバイダを追加する
1. Cognito Hosted UI を設定する

### アイデンティティプロバイダを追加する（オーバーライド）

管理コンソールから手でポチポチ設定しても良いのですが、せっかくなのでオーバーライドしてみます。``amplify override auth`` コマンドを実行し、amplify/backend/auth/<Resource ID>/override.ts を生成します。

```zsh
% amplify override auth
```

以下のコードを追加します。``Directory ID`` と ``Applicaiton ID`` は、適宜読み替えてください。

```JavaScript:override.ts
import { AmplifyAuthCognitoStackTemplate } from "@aws-amplify/cli-extensibility-helper";

export function override(resources: AmplifyAuthCognitoStackTemplate) {
  resources.addCfnResource(
    {
      type: "AWS::Cognito::UserPoolIdentityProvider",
      properties: {
        ProviderName: "AzureAD",
        ProviderType: "SAML",
        UserPoolId: resources.userPool.ref,
        AttributeMapping: {
          email:
            "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
        },
        ProviderDetails: {
          MetadataURL:
            "https://login.microsoftonline.com/<Directory ID>/federationmetadata/2007-06/federationmetadata.xml?appid=<Applicaiton ID>",
        },
      },
    },
    "SAML"
  );
}
```

``amplify push`` コマンドを実行し、設定変更を反映します。

```zsh
% amplify push
```

以上の手順で、AzureAD という名前の SAML アイデンティティプロバイダが追加されました。
![screenshot-ap-northeast-1.console.aws.amazon.com-2022.02.20-18_15_22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/17f49ac5-fde0-f636-6db2-5fdb6820fb49.png)

オーバーライドコードで指定した Attribute mapping や Metadata document も反映されています。
![screenshot-ap-northeast-1.console.aws.amazon.com-2022.02.23-17_08_10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/f83ec210-7489-6ee8-3b46-bc1a0ab2abf9.png)

### Cognito Hosted UI を設定する

続けて、App integration タブにある App client list から、Web 向けのアプリケーションクライアント（``_app_clientWeb`` で終わるもの）を選択します。
![screenshot-ap-northeast-1.console.aws.amazon.com-2022.02.20-10_02_15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/33aa8b9e-00b9-86d9-26ce-53b6c4d3b6a4.png)

選択したアプリケーションが表示されますので、下の方にスクロールしていきます。
![screenshot-ap-northeast-1.console.aws.amazon.com-2022.02.20-10_02_38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/f1a45365-e744-3413-e001-6256543fff6b.png)

Hosted UI の「Edit」を選択します。
![screenshot-ap-northeast-1.console.aws.amazon.com-2022.02.22-21_11_47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/6163d427-38d3-b8ea-f8ce-5eef6202eec1.png)

Identity providers として、先ほど作成した ``AzureAD`` のみを選択します。
![screenshot-ap-northeast-1.console.aws.amazon.com-2022.02.22-21_12_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/b7b30a90-3e5f-7569-246a-4a24a44d45ba.png)

「Save Changes」ボタンを選択します。以上の手順で、``AzureAD`` がアイデンティティプロバイダとして設定されました。
![screenshot-ap-northeast-1.console.aws.amazon.com-2022.02.22-21_12_22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/9a0671b4-3984-c7fc-20b5-53f4fbbc9cb1.png)

## Amplify アプリに認証を組み込む

やっと Amazon Cognito と Azure AD の設定が完了しましたので、いよいよ Amplify アプリに認証を組み込みます。

1. ソースコードを修正する
1. 修正版の動作確認する
1. フローをまとめておく

### ソースコードを修正する

``yarn create react-app`` で生成されたテンプレートに対して、以下のような修正を加えました。意味もなくダークテーマを適用しているため、少し長くなっていますが、要するに認証されていない場合、Coginito エンドポイントを呼び出すという少々乱暴なコードですが、ご容赦ください。

```JSX:App.tsx
import { useEffect, useState } from "react";
import Amplify, { Auth, Hub } from "aws-amplify";
import {
  createTheme,
  defaultTheme,
  AmplifyProvider,
  Button,
  Loader,
  Text,
} from "@aws-amplify/ui-react";
import "@aws-amplify/ui-react/styles.css";
import { CognitoUser } from "@aws-amplify/auth";

import logo from "./logo.svg";
import "./App.css";
import awsconfig from "./aws-exports";

Amplify.configure(awsconfig);

const App = () => {
  const [user, setUser] = useState<CognitoUser | null>(null);
  const [authInProgress, setAuthInProgress] = useState(false);

  const theme = createTheme({
    name: "dark-mode-theme",
    overrides: [
      {
        colorMode: "dark",
        tokens: {
          colors: {
            neutral: {
              // flipping the neutral palette
              10: defaultTheme.tokens.colors.neutral[100],
              20: defaultTheme.tokens.colors.neutral[90],
              40: defaultTheme.tokens.colors.neutral[80],
              80: defaultTheme.tokens.colors.neutral[40],
              90: defaultTheme.tokens.colors.neutral[20],
              100: defaultTheme.tokens.colors.neutral[10],
            },
            black: { value: "#fff" },
            white: { value: "#000" },
          },
        },
      },
    ],
  });

  useEffect(() => {
    const unsubscribe = Hub.listen("auth", ({ payload: { event, data } }) => {
      switch (event) {
        case "signIn":
          setUser(data);
          setAuthInProgress(false);
          break;
        case "signOut":
          setUser(null);
          setAuthInProgress(false);
          break;
        default:
      }
    });

    Auth.currentAuthenticatedUser()
      .then((user) => setUser(user))
      .catch(() => {
        window.location.href = `https://${awsconfig.oauth.domain}/login?response_type=${awsconfig.oauth.responseType}&client_id=${awsconfig.aws_user_pools_web_client_id}&redirect_uri=${awsconfig.oauth.redirectSignIn}`;
        setAuthInProgress(true);
      });

    return unsubscribe;
  }, []);

  return (
    <AmplifyProvider theme={theme} colorMode="dark">
      <div className="App">
        <header className="App-header">
          {authInProgress ? (
            <>
              <Loader width="5rem" height="5rem" />
              <Text>Signing in ...</Text>
            </>
          ) : (
            <>
              <img src={logo} className="App-logo" alt="logo" />
              <p>
                Edit <code>src/App.js</code> and save to reload.
              </p>
              <a
                className="App-link"
                href="https://reactjs.org"
                target="_blank"
                rel="noopener noreferrer"
              >
                Learn React
              </a>
              <br />
              <Button onClick={() => Auth.signOut()}>
                Sign Out ({user?.getUsername()})
              </Button>
            </>
          )}
        </header>
      </div>
    </AmplifyProvider>
  );
};

export default App;
```

### 修正版の動作確認する

``https://dev.<Applicaiton ID>.amplifyapp.com`` にアクセスします。一瞬、以下の画面が表示されて、Cognito Hosted UI に遷移します。
![screenshot-localhost_3000-2022.02.20-17_10_24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/93233cf4-43dd-5c8c-6817-9297a4de4839.png)

Cognito Hosted UI が表示されたら、「AzureAD」ボタンをクリックします。``AzureAD`` は、Cognito の Hosted UI に設定した Identity providers 名です。
![screenshot-ssowithaad-dev.auth.ap-northeast-1.amazoncognito.com-2022.02.20-16_18_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/6a32c93e-8eb9-f6dd-a318-0deacdd85c66.png)

既にサインイン済みのため、アカウントを選択します。もし、サインイン済みでない場合は、ユーザ名およびパスワードを入力することになります。
![screenshot-login.microsoftonline.com-2022.02.20-16_18_12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/8cba80f3-1d13-c262-604d-fbd190606996.png)

アプリにリダイレクトされ、「Sign Out」ボタンが表示されました。カッコ内は、Cognito User Pool の User name が表示されています。ここで「Sign Out」ボタンを選択すると、Cognito からサインアウトされて、再び Cognito Hosted UI が表示されます。
![screenshot-localhost_3000-2022.02.20-16_18_20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/ff9a1dab-55d5-5cda-307a-87551baaa492.png)

### フローをまとめておく

おおまかには、このようなフローとなります（非同期の API コールなどは、端折っています）。
![ssowithaad-flow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319241/5d838ac4-400c-6f6a-d868-fa43862264b5.png)

各エンドポイントは以下の通りです。手順は多かったですが、要するに「次にどこへ行けばいいのか」を順番に設定していたことが分かります。

|No|URL|説明|
|----|----|----|
|1|https\://dev.\<Applicaiton ID\>.amplifyapp.com|Amplify Hosting エンドポイント（アプリ）。Cognito Hosted UI の ``Allowed callback URLs`` および ``Allowed sign-out URLs`` に設定されている|
|2|https\://\<Cognito Domain\>.auth.ap-northeast-1.amazoncognito.com/login?response_type=code&client_id=\<Client ID\>&redirect_uri=\<Redirect URI\>|Amazon Cognito へのサインイン。未認証の場合、Hosted UI が表示される|
|3|https\://\<Cognito Domain\>.auth.ap-northeast-1.amazoncognito.com/oauth2/authorize?identity_provider=AzureAD&redirect_uri=\<Redirect URI\>&response_type=CODE&client_id=\<Client ID\>&scope=\<Scopes\>|Hosted UI の「Sign In」ボタン選択により、Cognito ユーザプールに Federated IdP として設定した ``AzureAD`` 設定に従い、OAuth フローが開始される|
|4|https\://login.microsoftonline.com/\<Directory ID\>/saml2?SAMLRequest=\<Token\>&RelayState=\<Token\>|マイクロソフトのサインイン UI が表示される。未認証の場合は、ユーザおよびパスワードを入力。認証済みの場合は、サインイン済みのアカウントリストから選択する。Cognito の SAML  IdP として追加した　``AzureAD`` の ``Metadata document`` に指定した URL から返される XML 内で ``SingleSignOnService`` の ``Location`` に設定されている|
|5|https\://login.microsoftonline.com/\<Directory ID\>/login|Azure AD へのサインイン。認証に成功した場合、サインイン状態を維持するかどうかを選択する|
|6|https\://\<Cognito Domain\>.auth.ap-northeast-1.amazoncognito.com/saml2/idpresponse|Amazon Cognito へのフェデレーテッドサインイン。アプリへの認可コード（``?code=<Grant Code>``）付きのアプリ URL が返される。Azure AD エンタープライズアプリケーションの ``Reply URL (Assertion Consumer Service URL)`` に設定されている|
|7|https\://dev.\<Applicaiton ID\>.amplifyapp.com/?code=\<Grant Code\>|Amplify Hosting エンドポイント（アプリ）|

## さいごに

AWS Amplify との出会いは遅めだったのですが、幸いエンタープライズ界隈の人間ですので、相対的に早めになっている気がします（良いか悪いかは別として）。AWS Amplify を採用したアジャイルプロジェクトがもたらす破壊的なアジリティは、非常に魅力的です。今後より一層、AWS Amplify のエンタープライズ採用が進むことを大いに期待しています。その意味でも、「エンタープライズあるある」である当記事がお役に立てば幸いです。

「はじめに」で触れたように、本記事は、「[Azure ADをAWS Cognito Federated IdPに追加して、Amplify+Vueアプリでログインする（模索中）](https://qiita.com/ksukenobe/items/a80e0e4435bcddd5e379)」の続編となります。前編の投稿から 2 年 7 ヶ月（！）が経過していることに驚きを禁じ得ません。その間に、AWS Amplify も React も大きな進化を遂げ、AWS や Azure も非常に多くの機能が追加・改善されました。少し足を止めて当記事をまとめましたが、引き続き、終わりなきキャッチアップの旅に出たいと思います。

### 参考リンク

- [Azure ADをAWS Cognito Federated IdPに追加して、Amplify+Vueアプリでログインする（模索中）](https://qiita.com/ksukenobe/items/a80e0e4435bcddd5e379)
- [Amplify UI 公式ドキュメント / Authenticator](https://ui.docs.amplify.aws/components/authenticator?platform=react)
- [Amplify 公式ドキュメント / Auth events] (https://docs.amplify.aws/lib/auth/auth-events/q/platform/js/)
- [Amazon Cognito デベロッパーガイド / ユーザープールへの SAML ID プロバイダーの追加](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-user-pools-saml-idp.html)