Go
Webhook の検証
配信の内容を信頼する前に、その署名を確認します。
配信を検証する
package main import ( "io" "log" "net/http" "os" "github.com/bfzli/openemail-go") func main() { secret := os.Getenv("OPENEMAIL_WEBHOOK_SECRET") http.HandleFunc("/webhooks/openemail", func(writer http.ResponseWriter, request *http.Request) { payload, err := io.ReadAll(http.MaxBytesReader(writer, request.Body, 1<<20)) if err != nil { writer.WriteHeader(http.StatusBadRequest) return } event, err := openemail.VerifyWebhookSignature(payload, request.Header, secret) if err != nil { writer.WriteHeader(http.StatusBadRequest) return } log.Println(event.String("type"), event.ID()) writer.WriteHeader(http.StatusNoContent) }) log.Fatal(http.ListenAndServe(":8080", nil))}openemail.VerifyWebhookSignature は X-OpenEmail-Signature ヘッダーを読み、タイムスタンプとボディに対する HMAC を定数時間で確認し、イベントを openemail.Object として返します。シークレットは client.Webhooks.Create が返したものです。
届いたままの生のボディを渡してください。解析して再エンコードしたボディは、もう署名と一致しません。
古い配信
payload := []byte(`{"id":"evt_1","type":"email.received"}`)headers := http.Header{"X-OpenEmail-Signature": {"t=1767225600,v1=5f2d"}} _, err := openemail.VerifyWebhookSignature(payload, headers, "whsec_example", openemail.WithTolerance(time.Minute)) if errors.Is(err, openemail.ErrWebhookSignature) { fmt.Println("refused:", err)}5 分以上前の配信は拒否されるため、傍受されたリクエストをあとで再送することはできません。openemail.WithTolerance でこの 5 分を変更でき、ゼロにするとどれだけ古い配信でも受け付けます。
- ヘッダーがない、または形式が正しくない場合、署名が一致しない場合、配信が古すぎる場合は、いずれも
openemail.ErrWebhookSignatureに一致するエラーを返します。 - シークレットをローテーションしたあとは、配信に複数の署名が付くことがあり、どれか 1 つが一致すれば通ります。
- すばやく 2xx のステータスで応答してください。それ以外の応答を受けた配信は、あとで再試行されます。