github.com/whoyao/protocol@v0.0.0-20230519045905-2d8ace718ca5/webhook/verifier.go (about)

     1  package webhook
     2  
     3  import (
     4  	"crypto/sha256"
     5  	"encoding/base64"
     6  	"io"
     7  	"net/http"
     8  
     9  	"google.golang.org/protobuf/encoding/protojson"
    10  
    11  	"github.com/whoyao/protocol/auth"
    12  	"github.com/whoyao/protocol/livekit"
    13  )
    14  
    15  // Receive reads and verifies incoming webhook is signed with key/secret pair
    16  // closes body after reading
    17  func Receive(r *http.Request, provider auth.KeyProvider) ([]byte, error) {
    18  	defer r.Body.Close()
    19  	data, err := io.ReadAll(r.Body)
    20  	if err != nil {
    21  		return nil, err
    22  	}
    23  
    24  	authToken := r.Header.Get(authHeader)
    25  	if authToken == "" {
    26  		return nil, ErrNoAuthHeader
    27  	}
    28  
    29  	v, err := auth.ParseAPIToken(authToken)
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  
    34  	secret := provider.GetSecret(v.APIKey())
    35  	if secret == "" {
    36  		return nil, ErrSecretNotFound
    37  	}
    38  
    39  	claims, err := v.Verify(secret)
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  
    44  	// verify checksum
    45  	sha := sha256.Sum256(data)
    46  	hash := base64.StdEncoding.EncodeToString(sha[:])
    47  
    48  	if claims.Sha256 != hash {
    49  		return nil, ErrInvalidChecksum
    50  	}
    51  
    52  	return data, nil
    53  }
    54  
    55  // ReceiveWebhookEvent reads and verifies incoming webhook, and returns a parsed WebhookEvent
    56  func ReceiveWebhookEvent(r *http.Request, provider auth.KeyProvider) (*livekit.WebhookEvent, error) {
    57  	data, err := Receive(r, provider)
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  	unmarshalOpts := protojson.UnmarshalOptions{
    62  		DiscardUnknown: true,
    63  		AllowPartial:   true,
    64  	}
    65  	event := livekit.WebhookEvent{}
    66  	if err = unmarshalOpts.Unmarshal(data, &event); err != nil {
    67  		return nil, err
    68  	}
    69  	return &event, nil
    70  }