【S3 + AzureAD】静的ページでSSOする方法

# このページは
S3にホスティングした静的ウェブサイトへのアクセス時にAzureADで認証を行うように実装する手順を書いています。

# 前提
- S3に静的ページがホスティングされていること
- CloudFront経由でアクセスできていること
- Route53にホストゾーンを作成し、独自ドメインで静的ウェブサイトにアクセスできること

# 構成
[Widen/cloudfront-auth](https://github.com/Widen/cloudfront-auth)を使って実現します。
今回はAzureADで認証を行いますが、Widen/cloudfront-authはAzureADの他にもGoogle Apps(G Suite), GitHub, OKTA, Auth0, Centrifyに対応できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397203/e6afd95a-4032-de73-2e4f-4759984d1693.png)

# 手順
1. AzureADにアプリケーションを作成
2. [Widen/cloudfront-auth](https://github.com/Widen/cloudfront-auth)をClone
3. Lambda関数を生成
4. Lambda関数をデプロイ
5. CloudFrontにLambda\@Edgeを設定
6. AzureADのアプリケーションにアクセスを許可したいユーザーまたはグループを割り当て
7. アクセスしてみる

## AzureADにアプリケーションを作成
[Azure portal](https://portal.azure.com/)にログインし、Azure Active Directoryのホーム画面を開きます。
[アプリの登録]> [新規登録]を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397203/c88ae8f9-c298-1af3-3aed-da3af184fe46.png)

#### アプリケーションの名前を入力（任意）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397203/ff328d74-e289-a793-d6ca-aae14b9eefed.png)

#### アプリケーションID（クライアントID）をメモ
このあと使うので控えておいてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397203/fa70843c-fd7c-63db-c773-0c740cbd5131.png)

#### リダイレクトURIを追加
`https://【ドメイン】/callback`と入力してください。

#### 資格情報を追加
`証明書とシークレット`を選択し、クライアントシークレットのタブを選択します。
`新しいクライアントシークレット`をクリックすると作成画面が開きます。
説明と有効期限を入力して追加ボタンをクリックします。
シークレットIDと値が生成されます。この値はこのあと使用するため控えておいてください。

:::note warn
この値は作成直後しか閲覧できません。
:::

AzureADのアプリケーションの設定はここまで

## [Widen/cloudfront-auth](https://github.com/Widen/cloudfront-auth)をClone
```
git clone https://github.com/Widen/cloudfront-auth.git
cd cloudfront-auth
```

## Lambda関数を生成
次にLambda関数を生成するスクリプトを実行します。
```
./build.sh
```
Lambda のディストリビューション名を指定する。(任意の名前)
```
>: Enter distribution name:  cloudfront-auth-xxx (例)
```
使用する認証プロバイダ（IdP）を選択
```
>: Authentication methods:
    (1) Google
    (2) Microsoft
    (3) GitHub
    (4) OKTA
    (5) Auth0
    (6) Centrify
    (7) OKTA Native

    Select an authentication method:  2
```
クライアント認証情報を入力
```
>>: Tenant:  AzureADテナントのIDを入力する
>>: Client ID:  クライアントID（アプリケーションID）を入力する
>>: Client Secret:  クライアントシークレットの値を入力する
```
Callback URL を入力
```
>>: Redirect URI: AzureADのアプリケーションを作ったときに入力したCallbackURLを入力する
```
認証の有効期限を入力
デフォルトの 0 だとすぐに期限切れとなって全くアクセスできないので注意。
```
>>: Session Duration (hours):  (0) 12
```
認可の設定
```
>>: Authorization methods:
   (1) Azure AD Login (default)
   (2) JSON Username Lookup

   Select an authorization method:  1
```
完了
生成された関数は、distributions/{distribution name}/{distribution name}.zip に出力される。
```
Done... created Lambda function distributions/cloudfront-auth-xxx/cloudfront-auth-xxx.zip
```

## Lambda関数をデプロイ
デプロイ方法はAWS SAMを使う方法と手動で行う方法があります。
今回は手動で行います。

### 手動でデプロイ
AWSのLambda関数の作成画面を開きます。
リージョンは、us-east-1を使用する。
作成画面では「設計図の使用」を選択します。
ブループリントのフィルタリングに`cloudfront-http-redirect`と入力します。
「Return HTTP redirect response」を選択し、設定ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397203/2533541d-f928-a149-cca5-b26173692444.png)
関数名とロール名を入力し、作成します。（名前は任意）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397203/44414c4f-7373-e48c-9a05-448d0514b7b9.png)
以下の画面が表示されたら、一旦キャンセルします。
CloudFrontへのLambda\@Edgeを設定する手順は後で行うためです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397203/f5c2d0b7-240a-1575-57d5-d9c4647d5ee0.png)

次に、タイムアウトを5秒に変更します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397203/eca880a4-ec61-bb7d-63ce-9a51a1152d59.png)

作成したZIPファイルをアップロードします。
![48823bcf-f416-43c3-9cf9-b6e54efbb9d9_png__3268×1280_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397203/21e991db-2921-c69d-456e-f58942041419.png)


ランタイムを変更します。今回は`Node.js 16.x`にします。（2022/11現在、Node.js 18.xはサポートされていないため）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397203/c7549bb5-44a9-8996-55cb-b1c417e32b4a.png)

バージョンを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397203/683c258b-f0db-2415-2e8c-dbb4d56c4fb8.png)

バージョンの説明欄を入力します。（例: v1）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397203/375b917f-c8fa-bf5c-b054-16a97aeef6af.png)

バージョンを作成したら、LambdaのARNを控えておきます。
次はCloudFrontにLambda\@Edgeを設定します。

## CloudFrontにLambda\@Edgeを設定
1. ホスティングしている CloudFront ディストリビューションの設定を開く
2. Default Behaviors を Edit
3. 「Lambda Function Associations（関数の関連付け）」に以下を追加して保存


CloudFront Event | Lambda Function ARN | Include Body
| --- | --- | --- |
Viewer Request | arn:aws:lambda:us-east-1:(アカウント番号):function:(関数名):1 | チェックしない |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397203/cdd3b6b7-4144-2049-08ee-ccee4b211179.png)

更新してデプロイし直した場合、Lambda関数の上がっていくので、その都度ここに設定するARNも変更する必要があります。
CloudFrontの設定が反映されるまではしばらく時間がかかります。
ディストリビューション一覧からステータスを確認できます。

## AzureADのアプリケーションにアクセスを許可したいユーザーまたはグループを割り当て
AzureADのユーザーやグループに静的サイトへのアクセスを許可します。
AzureADに移動します。
「管理」から、 「エンタープライズ アプリケーション」>「すべてのアプリケーション]」を選択します。
先程作成したアプリケーションを選択します。
アプリケーションの概要から「ユーザーとグループの割り当て」をクリックします。（または「管理」から「ユーザーとグループ」をクリック）
![Cursor_と_93b4488e-03a7-40db-92eb-a016a14cf666_png__2026×1088_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397203/19440efc-4b52-cda9-f55e-df1aa4813ce7.png)

割当画面が開きます。
必要に応じてユーザーやグループを割り当ててください。

## アクセスしてみる
静的ウェブサイトのURLにアクセスしたとき、AzureADの画面が出るのが期待値。
以下の画面が表示されたら承諾する。
![Cursor_と_【OIDC】S3にホスティングした静的ページにAzureAD経由でログインする_-_DocBase.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397203/c8a94341-07cc-b1ab-3126-f2e1b47dc66e.png)

AzureADのログインが成功すると...

キターーーーー！
![Cursor_と_【OIDC】S3にホスティングした静的ページにAzureAD経由でログインする_-_DocBase.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397203/236380c1-5b98-8866-670e-6264da674b6c.png)

手順は以上です。お疲れさまでした！

# 参考
- [CloudFront で Lambda@Edge を使って OpenID Connect (OIDC) 認証](https://zenn.dev/yh1224/articles/1tt26x8znv1ksoofe)
- [クイック スタート:Microsoft ID プラットフォームにアプリケーションを登録する](https://learn.microsoft.com/ja-JP/azure/active-directory/develop/quickstart-register-app)