ドキュメント本文へスキップ
Go

Webhook の検証

配信の内容を信頼する前に、その署名を確認します。

配信を検証する

webhook.go
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 が返したものです。

届いたままの生のボディを渡してください。解析して再エンコードしたボディは、もう署名と一致しません。

古い配信

tolerance.go
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 のステータスで応答してください。それ以外の応答を受けた配信は、あとで再試行されます。