Client-side Token Generation
#
Installation
#
go get github.com/SecureWebhookToken/swt@v0.4.0
Basic Token Creation and Sending
#
package main
import (
"fmt"
"log"
"net/http"
"github.com/SecureWebhookToken/swt"
)
func main() {
secretKey := []byte("your-secret-key-min-256-bits")
// Create a webhook request with payload
req := &swt.Request{
URL: "https://api.example.com/webhook",
Issuer: "webhook-service.example.com",
Event: "user.created",
HashAlg: swt.SHA256, // Optional: defaults to SHA-256
Data: []byte(`{"userId":"12345","email":"user@example.com"}`),
}
// Build the HTTP request with embedded SWT token
httpReq, err := req.Build(secretKey)
if err != nil {
log.Fatalf("Failed to build request: %v", err)
}
// Send the request
client := &http.Client{}
resp, err := client.Do(httpReq)
if err != nil {
log.Fatalf("Failed to send request: %v", err)
}
defer resp.Body.Close()
fmt.Printf("Response status: %s\n", resp.Status)
}
Token Creation with Empty Body
#
package main
import (
"fmt"
"log"
"net/http"
"github.com/SecureWebhookToken/swt"
)
func main() {
secretKey := []byte("your-secret-key-min-256-bits")
// Create a webhook request without payload (empty body)
req := &swt.Request{
URL: "https://api.example.com/webhook",
Issuer: "webhook-service.example.com",
Event: "health.check",
Data: nil, // No payload
}
httpReq, err := req.Build(secretKey)
if err != nil {
log.Fatalf("Failed to build request: %v", err)
}
client := &http.Client{}
resp, err := client.Do(httpReq)
if err != nil {
log.Fatalf("Failed to send request: %v", err)
}
defer resp.Body.Close()
fmt.Printf("Health check response: %s\n", resp.Status)
}
Advanced Token Creation with Options
#
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/SecureWebhookToken/swt"
"github.com/google/uuid"
)
func main() {
secretKey := []byte("your-secret-key-min-256-bits")
// Create token with custom options
req := &swt.Request{
URL: "https://api.example.com/webhook",
Issuer: "webhook-service.example.com",
Event: "payment.received",
HashAlg: swt.SHA384, // Use SHA-384 instead of default SHA-256
Data: []byte(`{"amount":100.00,"currency":"USD"}`),
}
// Build with custom options
ctx := context.Background()
httpReq, err := req.BuildWithContext(ctx, secretKey,
swt.WithID(uuid.New().String()), // Custom JWT ID
swt.WithSubject("user-12345"), // Subject identifier
swt.WithExpiresAt(time.Now().Add(5*time.Minute)), // 5 minute expiry
swt.WithNotBefore(time.Now()), // Valid immediately
swt.WithRetryCount(0), // First delivery attempt
)
if err != nil {
log.Fatalf("Failed to build request: %v", err)
}
client := &http.Client{}
resp, err := client.Do(httpReq)
if err != nil {
log.Fatalf("Failed to send request: %v", err)
}
defer resp.Body.Close()
fmt.Printf("Payment webhook sent: %s\n", resp.Status)
}
Creating Token Manually (Low-level API)
#
package main
import (
"fmt"
"log"
"time"
"github.com/SecureWebhookToken/swt"
"github.com/google/uuid"
)
func main() {
secretKey := []byte("your-secret-key-min-256-bits")
// Create hash for the payload
payload := []byte(`{"userId":"12345"}`)
hash := swt.NewHash(swt.SHA256, payload)
// Create token with event and hash
token, err := swt.New(
"webhook-service.example.com", // issuer
"user.created", // event
hash, // payload hash
swt.WithID(uuid.New().String()),
swt.WithExpiresAt(time.Now().Add(5*time.Minute)),
swt.WithNotBefore(time.Now()),
swt.WithSubject("user-12345"),
)
if err != nil {
log.Fatalf("Failed to create token: %v", err)
}
// Sign the token
tokenString, err := token.SignedString(secretKey)
if err != nil {
log.Fatalf("Failed to sign token: %v", err)
}
fmt.Printf("Token: %s\n", tokenString)
fmt.Printf("Event: %s\n", token.Webhook().Event)
fmt.Printf("Hash: %s\n", token.Webhook().Hash.String())
}
Retry Logic with Retry Count
#
package main
import (
"fmt"
"log"
"net/http"
"time"
"github.com/SecureWebhookToken/swt"
)
func sendWebhookWithRetry(req *swt.Request, secretKey []byte, maxRetries uint) error {
client := &http.Client{Timeout: 30 * time.Second}
for retry := uint(0); retry <= maxRetries; retry++ {
// Build request with current retry count
httpReq, err := req.Build(secretKey, swt.WithRetryCount(retry))
if err != nil {
return fmt.Errorf("failed to build request: %w", err)
}
resp, err := client.Do(httpReq)
if err != nil {
log.Printf("Attempt %d failed: %v", retry, err)
if retry < maxRetries {
time.Sleep(time.Duration(retry+1) * 2 * time.Second) // Exponential backoff
continue
}
return fmt.Errorf("all retry attempts failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
fmt.Printf("Webhook delivered successfully on attempt %d\n", retry)
return nil
}
log.Printf("Attempt %d returned status %d", retry, resp.StatusCode)
if retry < maxRetries {
time.Sleep(time.Duration(retry+1) * 2 * time.Second)
}
}
return fmt.Errorf("webhook delivery failed after %d attempts", maxRetries)
}
func main() {
secretKey := []byte("your-secret-key-min-256-bits")
req := &swt.Request{
URL: "https://api.example.com/webhook",
Issuer: "webhook-service.example.com",
Event: "order.completed",
Data: []byte(`{"orderId":"ORD-12345","total":250.00}`),
}
if err := sendWebhookWithRetry(req, secretKey, 3); err != nil {
log.Fatalf("Webhook delivery failed: %v", err)
}
}