Azure MonitorでAzure AD B2Cのトレースログを取得する（②実践編）

Azure AD B2CとAzure Monitorを連携することで、Azure AD B2Cにサインインした際、どういった情報がログから取得できるか、
逆にどういった情報は取得できないかをご紹介します。

本記事ではLog Analyticsと連携し、B2Cのトレースログを収集します。

環境構築については、下記の記事をご参照ください。

[Azure MonitorでAzure AD B2Cのトレースログを取得する（①環境構築編）](https://qiita.com/AsukaSeki/items/7f9f05491c9c73c2178c)

### B2Cのログの保持期間について
Log Analyticsとの連携無しでも、直近の情報であればAzure PortalのB2C管理画面からログを確認することができます。

B2Cの管理画面からユーザーを選択し、「監査ログ」や「サインインログ」から確認できます。
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398865/49ada5a8-e948-b165-6ca0-0507c30794bd.png)


ただしこのログの最大保持期間は7日間となるため、さらに保持期間を延長させる場合はAzure Monitorとの連携が必要です。

既定ではAzure Monitorでは30日間ログが保持されますが、Azure Monitorの価格レベルを上げることで保持期間は最大2年間延長できます。

参考：https://docs.microsoft.com/ja-jp/azure/active-directory-b2c/azure-monitor#change-the-data-retention-period

### B2Cのログの内容について
上述の通り、B2Cのログには「監査ログ」と「サインインログ」の2種類があります。

#### 監査ログ
監査ログでは、認証した日付、サインインした端末の情報、サインインの成功・失敗などの情報が取得できます。
どういった情報が取得できるかは後述します。

参考：https://docs.microsoft.com/ja-jp/azure/active-directory-b2c/view-audit-logs

#### サインインログ
サインインの成功・失敗とその理由などが取得できます。

例えば「パスワードの入力に失敗した」「パスワードの失敗回数が既定の回数を超えた」といったエラーは取得できるものの、
2要素認証での確認コードの入力失敗や、カスタムAPIのエラーなどといった「メールアドレス/パスワードの認証」以降のフローについての失敗は検出できず、このようなケースはすべて「成功」と検出されてしまうようです。

そのため今回は監査ログを用いてB2Cから取れる様々な情報を取得していきます。

### 監査ログから取得できる情報
本記事では、メールアドレスとパスワードによるサインアップ/サインインを行うユーザーフローorカスタムポリシーを前提としています。

#### サインイン
B2Cにサインインし、その後Log Analyticsで下記のクエリを実行します。（反映に5分程度かかります）
```
AuditLogs
| order by TimeGenerated desc 
```

上記クエリを実行すると、このように一覧で表示ができます。
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398865/dae6fa1e-c1a3-c641-bb3d-b934ed30ec2b.png)

色々な情報が取れますが、中でも下記の情報が特に有用です。

* TimeGenerated
* TenantId: B2CテナントID
* CorrelationId: サインインフローごとに発行されるID
* OperationName
* Result: サインインの成功・失敗
* AdditionalDetails
    * PolicyId: B2CポリシーID
    * ApplicationId: B2CアプリケーションID
    * Client: サインインした端末のUserAgent
    * ClientIpAddress: サインインした端末のIPアドレス
* TargetResources
    * Id: サインインしたユーザーのオブジェクトID

![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398865/969cf6ba-fe70-16ac-4117-ac98de57ab0c.png)
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398865/727976b0-ea83-a23e-b31a-88cb13cc8e4e.png)

また、OperationNameはこういったものが取れるようです。

1. Validate local account credentials: アカウントの検証が行われた
1. Issue an id_token to the application: 認証が完了し、トークンが発行された

そして、これらの一連のログは`CorrelationId`で特定することができます。
つまり、同じアカウントであっても違う時間や端末でサインインすると、それぞれ別のCorrelationIdが振られます。

B2Cのポリシー名やObject Idは入れ子になっている要素にありますので、それをログに出すにはこのようなクエリを実行します。

また、監査ログはB2C管理ページへのサインインやユーザーストーリーの作成・変更などといった管理ページ内の操作についてのログも出てしまいます。

そのためB2Cのサインインのみに関するログを抽出したい場合、ポリシーIDでフィルタリングしましょう。

```
AuditLogs
| extend ObjectId=extractjson("$.[0].id",tostring(TargetResources))                 // Object ID
| extend PolicyId=extractjson("$.[1].value",tostring(AdditionalDetails))            // Policy ID
| where PolicyId startswith "B2C_"
| order by TimeGenerated desc  
```

上記クエリを実行すると、出力結果にObjectIdやPolicyIdが出力されることが確認できます。
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398865/f49a27b0-fa42-a0ae-5ce9-dab94e31e898.png)


#### 2要素認証をしたサインイン
次に、上記に加え2要素認証（SMS）を含んだサインインを実行します。

その後、LogAnalyticsで先ほどのクエリを実行するとこのような結果が取得できます。
![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398865/9f27517e-a2d7-2ea6-3d8e-7b88453a7d5a.png)


OperationNameを見ると、さきほどのサインイン時の情報に加えSMSが送信されたり電話番号の検証がされた情報も取得できます。

1. Validate local account credentials
1. **Send SMS to verify phone number**: SMSが送信された
1. **Verify phone number**: 電話番号の検証が行われた
1. Issue an id_token to the application

#### サインアップ
次に、サインアップを行った後に先ほどのクエリを実行するとこのような結果が取得できます。
![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398865/c5f7420b-a419-d941-e9cb-bb2cd31e6cf7.png)


「メールアドレスの検証が行われた」「2要素認証が行われた」というところまでは確認できるものの、このままではサインアップによるものか、またはパスワードの再設定によるものか判断ができません。

そこで、クエリの内容を変えてみましょう。
```
AuditLogs
| extend ObjectId=extractjson("$.[0].id",tostring(TargetResources))                 // Object ID
| extend PolicyId=extractjson("$.[1].value",tostring(AdditionalDetails))            // Policy ID
| where ObjectId == "<オブジェクトID>" or CorrelationId == "<CorrelationId>"
| order by TimeGenerated desc 
```

結果はこのようになります。ユーザーが追加されたログや2要素認証によりユーザー情報が変更された（電話番号が登録された）ログが取得できます。

ただし、このときPolicyIdは空になり、CorrelationIdはそれぞれ別のIDが発行されるようです。
![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398865/f40dea62-ec68-0fed-c270-2d5eff0e7cc4.png)

### ログから取得できない情報
B2Cの監査ログで、いつ、誰が、どのポリシーを実行し、成功したかどうかを取得することができますが、画面単位やUserJourney（カスタムポリシーの場合）単位でのログ出力はできません。

そのため、例えば下記のようなケースをログに出すには、B2Cのログのみでなく他のサービスとも組み合わせる必要があります。
* ログイン画面でログインボタンをクリックした、などのクリックイベント追跡
    * もしこちらを実現したい場合は、下記のようなApplication InsightsプラグインをHTMLに組み込めば実現できそうです。
       * 参考：https://docs.microsoft.com/ja-jp/azure/azure-monitor/app/javascript-click-analytics-plugin
    * また、CorrelationIdは下記ドキュメントの通りB2CのHTMLのコメントに記載されているため、CorrelationIdの取得も可能です。
       * 参考：https://docs.microsoft.com/ja-jp/azure/active-directory-b2c/troubleshoot?pivots=b2c-custom-policy#get-the-azure-ad-b2c-correlation-id
* 内部で呼び出すカスタムAPIの成功・失敗（失敗した場合はその理由など）
    * ただしカスタムポリシーの場合は要求リゾルバーを利用することでAPI呼び出し時にCorrelationIdを渡すことができます
       * 参考：https://docs.microsoft.com/ja-jp/azure/active-directory-b2c/claim-resolver-overview
    * そのため、B2CのログだけでなくAPI側のログとうまく組み合わせることでどのユーザーがいつどういったエラーで失敗したかを特定することは可能です

## 関連記事
* 前回の記事：[Azure MonitorでAzure AD B2Cのトレースログを取得する（①環境構築編）](https://qiita.com/AsukaSeki/items/7f9f05491c9c73c2178c)
* [AD B2C記事まとめ](https://qiita.com/AsukaSeki/items/46afb6846cb4057b8050)

## 参考資料
* [Azure Monitor で Azure AD B2C を監視する](https://learn.microsoft.com/ja-jp/azure/active-directory-b2c/azure-monitor)
* [Azure AD B2C 監査ログへのアクセス](https://learn.microsoft.com/ja-jp/azure/active-directory-b2c/view-audit-logs)