[Auth0] エラーログを Slack 通知する
概要
業務で認証プラットフォームとして Auth0 を使用しています。
そこでエラーログを Slack 通知する必要があったのですが、実装方法を説明している記事等が中々見つからず苦労したため、実際に構築したフローを紹介したいと思います。
前提
この記事では、以下の技術を使用して構築を行います。
詳しい解説は省略しますので、概要は公式ドキュメントを参照してください。
フロー
実際に構築するフローと、各レイヤーの役割は以下のような感じです。
- Auth0 Log Streaming
- ログの1次フィルター
- ストリーム通知
- EventBridge
- ログの2次フィルター
- Lambda 関数をトリガー
- Lambda
- ログの整形
- Slack 通知
実装
では実際に構築手順を解説していきます。
今回は Auth0 と AWS のリソースに関しては、コンソールから作成する方法と Terraform で作成する方法の両方を解説します。
サンプルコードは以下 GitHub リポジトリに用意しています。
Auth0 Log Streaming
まずは Auth0 の Log Streaming の設定からはじめていきます。
ドキュメントにある通り、この機能では Auth0 のイベントログをリアルタイムに連携先の外部サービスに配信することができます。
Auth0 コンソールの Monitoring > Streams
から Create Log Stream
のボタンを押下し、Amazon EventBridge
を選択して任意の名前で Event Stream を作成します。
Settings
から今回使用する AWS アカウントの ID
Region
を入力し、Filter by Event Category
のセレクトボックスから通知したいイベントを選択します。(ログの1次フィルター設定)
各イベントの詳細は以下公式ドキュメントを参照してください。
今回は例として Failure 系のイベントを全て設定していきます。
設定が終わったら Save
ボタンを押せば Auth0 での設定は完了です。
Terraform で構築する場合はこちら
resource "auth0_log_stream" "event_bridge" {
name = "Amazon EventBridge"
type = "eventbridge"
status = "active"
filters = [
{
"name" = "auth.login.fail"
"type" = "category"
},
{
"name" = "auth.logout.fail"
"type" = "category"
},
{
"name" = "auth.signup.fail"
"type" = "category"
},
{
"name" = "auth.silent_auth.fail"
"type" = "category"
},
{
"name" = "auth.token_exchange.fail"
"type" = "category"
},
{
"name" = "management.fail"
"type" = "category"
},
{
"name" = "user.fail"
"type" = "category"
}
]
sink {
aws_account_id = var.aws_account_id
aws_region = "ap-northeast-1"
}
}
variable "aws_account_id" {
type = string
}
Serverless (Lambda)
次に Slack への通知処理を行う Lambda 関数を作成していきます。
Serverless を使用して構築するため、まだ持ってない方は serverless
module をインストールして下さい。
npm install -g serverless
今回は aws-node-typescript (v3)
のテンプレートを使用します。
> serverless create --template-url https://github.com/serverless/examples/tree/v3/aws-node-typescript --path auth0-alert-to-slack
✔ Project successfully created in "auth0-alert-to-slack" (2s)
> tree .
.
├── README.md
├── handler.ts
├── package.json
└── serverless.yml
5 directories, 13 files
SlackAPI を使用するため、@slack/web-api
をインストールします。
npm install @slack/web-api
また SlackApp を作成し、以下の設定を行なって下さい。
-
OAuth & Permissions
からBot Token Scopes (chat:write)
を設定 -
Install to Workspace
ボタンから、アプリをワークスペースにインストール - 作成したアプリを通知するチャンネルに追加
@koki さんの以下の記事がとても分かりやすく参考になります。
Slack API を使用してメッセージを投稿する
これで準備は整ったので、実際に関数の中身を実装していきます。
Auth0 のログメッセージは、event.detail
に Json 形式で入っています。
今回はサンプルなので中身をそのまま Slack へと通知してみます。
import { WebClient } from '@slack/web-api'
export async function notify(event) {
const SLACK_BOT_TOKEN = process.env.SLACK_BOT_TOKEN
const SLACK_CHANNEL_ID = process.env.SLACK_CHANNEL_ID
const client = new WebClient(SLACK_BOT_TOKEN)
await client.chat.postMessage({
channel: SLACK_CHANNEL_ID,
blocks: [
{
type: "section",
text: {
type: "mrkdwn",
text: "```" + JSON.stringify(event.detail, null, 2) + "```"
}
}
]
})
}
service: auth0-alert-to-slack
frameworkVersion: '3'
provider:
name: aws
runtime: nodejs18.x
region: ap-northeast-1
functions:
notify:
handler: handler.notify
plugins:
- serverless-esbuild
これで実装は完了したので、serverless
コマンドでデプロイします。
serverless deploy --verbose
デプロイ後、以下の環境変数を設定しておいて下さい。
- SLACK_BOT_TOKEN
- SLACK_CHANNEL_ID
これで Serverless の構築は完了です。
EventBridge
最後に AWS の EventBridge の設定を行います。
EventBridge は Auth0 などのイベントソースからリアルタイムデータのストリームを配信し、そのデータを Lambda などのターゲットにルーティングすることができます。
まず AWS コンソールの EventBridge > Partner event source
を確認すると、先ほど設定した auth0 のイベントが表示されているかと思います。
イベントを選択し、 Associate with event bus
ボタンを押下するとカスタムイベントバスが作成され、Auth0 のイベントが送信されるようになります。
次にイベントルールを作成します。
EventBridge > Buses > Rules
から Create rule
ボタンで作成画面へと進み、以下を設定後 Next
ボタンを押下します。
- Name: 任意の名前
- Event bus: 先ほど作成したカスタムイベントバス
- Rule type: event pattern
するとルールの詳細画面に遷移すると思いますが、基本はデフォルトのまま Event Pattern の枠までスクロールし、
- Event source: EventBridge partners
- Partner: Auth0
を設定後、Edit Pattern
のボタンを押下します。
ここでログの2次フィルター設定を行います。
先ほどの Auth0 側の Log Streaming 設定では event category 単位でしか filter 設定ができなかったのですが、実際にはより細かい event type 単位での filter が必要となってきます。
Login Failure
を例にあげると
-
fp
: Incorrect password -
fu
: Invalid email or username
などのように、システム側ではどうしようもないユーザの操作ミスなどのエラーを通知するとキリがないため、これらの event type を通知しないよう filter していきます。
{
"account": ["000000000000"],
"detail": {
"data": {
"type": [{
"anything-but": ["fp", "fu"]
}]
}
}
}
※ Auth0 から配信されるログの Preview
{
"id": "623053cf-8e0f-9203-f464-e789cf18b0e2",
"detail-type": "Auth0 log",
"source": "aws.partner/auth0.com/example-tenant-635d694a-8a5a-4f1a-b223-1e0424edd19a/auth0.logs",
"account": "123456789012",
"time": "2020-01-29T17:26:50Z",
"region": "us-west-2",
"resources": [],
"detail": {
"log_id": "",
"data": {
"date": "2020-01-29T17:26:50.193Z",
"type": "sapi",
"description": "Create a log stream",
"client_id": "",
"client_name": "",
"ip": "",
"user_id": "",
"log_id": ""
}
}
}
anything-but
構文を使用して filter 設定しています。
EventBridge の Pattern 設定の詳細に関しては以下公式ドキュメントを参照してください。
最後に Event Target として先ほど作成した Lambda 関数を設定すれば Eventbridge の構築も完了です。
Terraform で構築する場合はこちら
resource "aws_cloudwatch_event_bus" "auth0_error_log" {
name = var.auth0_event_name
event_source_name = var.auth0_event_name
}
resource "aws_cloudwatch_event_rule" "auth0_error_log" {
name = "auth0-error-log"
event_bus_name = aws_cloudwatch_event_bus.auth0_error_log.name
event_pattern = <<EOF
{
"account": ["${var.aws_account_id}"],
"detail": {
"data": {
"type": [ { "anything-but": [
"fp",
"fu"
] } ]
}
}
}
EOF
}
data "aws_lambda_function" "auth0_alert_to_slack" {
function_name = "auth0-alert-to-slack-dev-notify"
}
resource "aws_lambda_permission" "allow_auth0_alert_to_slack" {
action = "lambda:InvokeFunction"
function_name = data.aws_lambda_function.auth0_alert_to_slack.arn
principal = "events.amazonaws.com"
source_arn = aws_cloudwatch_event_rule.auth0_error_log.arn
}
resource "aws_cloudwatch_event_target" "auth0_error_log" {
rule = aws_cloudwatch_event_rule.auth0_error_log.name
event_bus_name = aws_cloudwatch_event_bus.auth0_error_log.name
target_id = "auth0-error-log"
arn = data.aws_lambda_function.auth0_alert_to_slack.arn
}
variable "aws_account_id" {
type = string
}
variable "auth0_event_name" {
type = string
}
まとめ
ここまでの手順を全て問題なく完了していれば、Auth0 で何かしらのエラーを発生させると Slack にエラーログが飛んでくるかと思います。
もっとより良い構築方法などありましたらコメントで教えていただけると幸いです。
Discussion