webhooks

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 29, 2025 License: MIT Imports: 10 Imported by: 0

README

Standard Webhooks Implementation in Go

A Go implementation of the Standard Webhooks specification. This package provides a secure, reliable, and standard way to sign and verify webhook payloads, making it easier for providers to send and consumers to receive webhooks.

Features

  • Full compliance with the Standard Webhooks specification
  • Multiple signature algorithms support:
    • ED25519 (asymmetric, v1a)
    • HMAC-SHA256 (symmetric, v1)
  • Multiple simultaneous signers and verifiers for graceful algorithm transitions
  • Protection against replay attacks with timestamp validation
  • Extensible interface for custom signing methods
  • Thread-safe implementation
  • Comprehensive test coverage

Installation

go get github.com/krofort/std-webhooks-go

Quick Start

import "github.com/krofort/std-webhooks-go"

// Create a signer and verifier with HMAC
key := []byte("your-secret-key")
signer := webhooks.NewWebhookSigner(webhooks.NewHMACSigner(key))
verifier := webhooks.NewWebhookVerifier(webhooks.NewHMACSigner(key))

// Sign a payload
signature, err := signer.Sign("message-id", time.Now(), []byte("your-payload"))
if err != nil {
    log.Fatal(err)
}

// Verify a payload
headers := http.Header{}
headers.Set(webhooks.HeaderWebhookID, "message-id")
headers.Set(webhooks.HeaderWebhookSignature, signature)
headers.Set(webhooks.HeaderWebhookTimestamp, strconv.FormatInt(time.Now().Unix(), 10))

err = verifier.Verify([]byte("your-payload"), headers)
if err != nil {
    log.Fatal(err)
}
// Generate or load your ED25519 keys
publicKey, privateKey, _ := ed25519.GenerateKey(rand.Reader)

// Create signer and verifier
signer := webhooks.NewWebhookSigner(webhooks.NewED25519Signer(privateKey))
verifier := webhooks.NewWebhookVerifier(webhooks.NewED25519Verifier(publicKey))
Multiple Signature Methods (For Algorithm Transitions)
// Create signers with both ED25519 and HMAC
signer := webhooks.NewWebhookSigner(
    webhooks.NewED25519Signer(privateKey),
    webhooks.NewHMACSigner(hmacKey),
)

// Create verifier with both ED25519 and HMAC
verifier := webhooks.NewWebhookVerifier(
    webhooks.NewED25519Verifier(publicKey),
    webhooks.NewHMACSigner(hmacKey),
)

// Or add them dynamically
signer.AddSigner(webhooks.NewHMACSigner(anotherKey))
verifier.AddVerifier(webhooks.NewHMACSigner(anotherKey))

Standard Webhooks Compliance

This implementation follows the Standard Webhooks specification for:

HTTP Headers

The package uses the standard webhook headers:

  • webhook-id: A unique identifier for the webhook message
  • webhook-signature: The signature(s) of the payload
  • webhook-timestamp: Unix timestamp of when the webhook was sent
Signature Format

The signature format follows the standard:

<version>,<base64-signature>

When using multiple signers, signatures are space-separated:

v1a,<base64-ed25519-sig> v1,<base64-hmac-sig>
Security Features
  • Replay Attack Prevention: Includes timestamp validation with a 5-minute tolerance
  • Multiple Signatures: Supports multiple signing algorithms for security and flexibility
  • Standard Cryptography: Uses well-tested Go crypto packages
  • Non-repudiation: Supports ED25519 for cryptographic proof of origin
  • HMAC Support: Provides symmetric key verification option

Implementing Custom Signers

You can implement custom signing methods by implementing the Signer and/or Verifier interfaces:

type Signer interface {
    Version() string
    Sign(message []byte) ([]byte, error)
}

type Verifier interface {
    Version() string
    Verify(message, signature []byte) error
}

Error Handling

The package provides standard error types:

  • ErrRequiredHeaders: Missing required headers
  • ErrInvalidHeaders: Invalid header format
  • ErrNoMatchingSignature: No valid signature found
  • ErrMessageTooOld: Message timestamp is too old
  • ErrMessageTooNew: Message timestamp is in the future
  • ErrNoSigners: No signers configured
  • ErrNoVerifiers: No verifiers configured

Best Practices

  1. Algorithm Choice:

    • Use ED25519 for production environments where non-repudiation is important
    • Use HMAC for simpler implementations or when sharing public keys is impractical
  2. Key Management:

    • Rotate keys periodically
    • Use environment variables or secure key management systems
    • Never hardcode secret keys
  3. Signature Verification:

    • Always verify timestamps to prevent replay attacks
    • Implement proper error handling for all verification steps
    • Consider using multiple signature methods during algorithm transitions

Contributing

Contributions are welcome! Please feel free to submit a Pull Request. Make sure to read the Standard Webhooks Specification before contributing.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Resources

Documentation

Index

Constants

View Source
const (
	HeaderWebhookID        string = "webhook-id"
	HeaderWebhookSignature string = "webhook-signature"
	HeaderWebhookTimestamp string = "webhook-timestamp"
)

Variables

View Source
var (
	ErrRequiredHeaders     = errors.New("missing required headers")
	ErrInvalidHeaders      = errors.New("invalid signature headers")
	ErrNoMatchingSignature = errors.New("no matching signature found")
	ErrMessageTooOld       = errors.New("message timestamp too old")
	ErrMessageTooNew       = errors.New("message timestamp too new")
	ErrInvalidKeyType      = errors.New("invalid key type")
	ErrNoSigners           = errors.New("no signers configured")
	ErrNoVerifiers         = errors.New("no verifiers configured")
)

Functions

This section is empty.

Types

type ED25519Signer

type ED25519Signer struct {
	// contains filtered or unexported fields
}

ED25519Signer implements the Signer interface using ED25519

func NewED25519Signer

func NewED25519Signer(key ed25519.PrivateKey) *ED25519Signer

NewED25519Signer creates a new ED25519 signer from a private key

func (*ED25519Signer) Sign

func (s *ED25519Signer) Sign(message []byte) ([]byte, error)

Sign signs the message using ED25519

func (*ED25519Signer) Version

func (s *ED25519Signer) Version() string

Version returns the version string for ED25519 signer

type ED25519Verifier

type ED25519Verifier struct {
	// contains filtered or unexported fields
}

ED25519Verifier implements the Verifier interface using ED25519

func NewED25519Verifier

func NewED25519Verifier(key ed25519.PublicKey) *ED25519Verifier

NewED25519Verifier creates a new ED25519 verifier from a public key

func (*ED25519Verifier) Verify

func (s *ED25519Verifier) Verify(message, signature []byte) error

Verify verifies the message using ED25519

func (*ED25519Verifier) Version

func (s *ED25519Verifier) Version() string

Version returns the version string for ED25519 verifier

type HMACSignerAndVerifier

type HMACSignerAndVerifier struct {
	// contains filtered or unexported fields
}

HMACSignerAndVerifier implements both Signer and Verifier interfaces using HMAC-SHA256

func NewHMACSignerAndVerifier

func NewHMACSignerAndVerifier(key []byte) *HMACSignerAndVerifier

NewHMACSignerAndVerifier creates a new HMAC signer/verifier from a secret key

func (*HMACSignerAndVerifier) Sign

func (s *HMACSignerAndVerifier) Sign(message []byte) ([]byte, error)

Sign signs the message using HMAC-SHA256

func (*HMACSignerAndVerifier) Verify

func (s *HMACSignerAndVerifier) Verify(message, signature []byte) error

Verify verifies the message using HMAC-SHA256

func (*HMACSignerAndVerifier) Version

func (s *HMACSignerAndVerifier) Version() string

Version returns the version string for HMAC signer

type Signer

type Signer interface {
	// Version returns the version string for this signer (e.g. "v1" or "v1a")
	Version() string
	// Sign signs the given message and returns the signature
	Sign(message []byte) ([]byte, error)
}

Signer represents a webhook signing implementation

type Verifier

type Verifier interface {
	// Version returns the version string for this verifier (e.g. "v1" or "v1a")
	Version() string
	// Verify verifies the given message with the signature
	Verify(message, signature []byte) error
}

Verifier represents a webhook verification implementation

type WebhookSigner

type WebhookSigner struct {
	// contains filtered or unexported fields
}

WebhookSigner handles webhook signing

func NewWebhookSigner

func NewWebhookSigner(signers ...Signer) *WebhookSigner

NewWebhookSigner creates a new webhook signer with multiple signers

func (*WebhookSigner) AddSigner

func (wh *WebhookSigner) AddSigner(s Signer)

AddSigner adds a new signer to the webhook signer

func (*WebhookSigner) Sign

func (wh *WebhookSigner) Sign(msgID string, timestamp time.Time, payload []byte) (string, error)

Sign signs the webhook payload with all configured signers

type WebhookVerifier

type WebhookVerifier struct {
	// contains filtered or unexported fields
}

WebhookVerifier handles webhook verification

func NewWebhookVerifier

func NewWebhookVerifier(verifiers ...Verifier) *WebhookVerifier

NewWebhookVerifier creates a new webhook verifier with multiple verifiers

func (*WebhookVerifier) AddVerifier

func (wh *WebhookVerifier) AddVerifier(v Verifier)

AddVerifier adds a new verifier to the webhook verifier

func (*WebhookVerifier) Verify

func (wh *WebhookVerifier) Verify(payload []byte, headers http.Header) error

Verify verifies the webhook payload and headers

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL