-
Notifications
You must be signed in to change notification settings - Fork 32
Expand file tree
/
Copy pathwebhook_verification_test.go
More file actions
126 lines (99 loc) · 4.21 KB
/
webhook_verification_test.go
File metadata and controls
126 lines (99 loc) · 4.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// @oagen-ignore-file
package workos_test
import (
"fmt"
"strconv"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/workos/workos-go/v8"
)
const testWebhookSecret = "whsec_test_secret_key"
// buildWebhookSigHeader computes a valid signature header for testing.
func buildWebhookSigHeader(secret string, body string, ts time.Time) string {
timestamp := strconv.FormatInt(ts.UnixMilli(), 10)
sig := workos.ComputeWebhookSignature(secret, timestamp, body)
return fmt.Sprintf("t=%s, v1=%s", timestamp, sig)
}
func TestVerifyPayload_ValidSignature(t *testing.T) {
body := `{"event":"user.created","data":{"id":"user_123"}}`
now := time.Now()
sigHeader := buildWebhookSigHeader(testWebhookSecret, body, now)
verifier := workos.NewWebhookVerifier(testWebhookSecret)
// Pin the time so the timestamp is within tolerance.
verifier.SetTolerance(5 * time.Minute)
result, err := verifier.VerifyPayload(sigHeader, body)
require.NoError(t, err)
require.Equal(t, body, result)
}
func TestVerifyPayload_InvalidSignature(t *testing.T) {
body := `{"event":"user.created"}`
now := time.Now()
timestamp := strconv.FormatInt(now.UnixMilli(), 10)
sigHeader := fmt.Sprintf("t=%s, v1=%s", timestamp, "badsignaturevalue")
verifier := workos.NewWebhookVerifier(testWebhookSecret)
_, err := verifier.VerifyPayload(sigHeader, body)
require.ErrorIs(t, err, workos.ErrWebhookNoValidSignature)
}
func TestVerifyPayload_ExpiredTimestamp(t *testing.T) {
body := `{"event":"user.created"}`
// Timestamp from 10 minutes ago (default tolerance is 180s).
old := time.Now().Add(-10 * time.Minute)
sigHeader := buildWebhookSigHeader(testWebhookSecret, body, old)
verifier := workos.NewWebhookVerifier(testWebhookSecret)
_, err := verifier.VerifyPayload(sigHeader, body)
require.ErrorIs(t, err, workos.ErrWebhookOutsideTolerance)
}
func TestVerifyPayload_EmptyHeader(t *testing.T) {
verifier := workos.NewWebhookVerifier(testWebhookSecret)
_, err := verifier.VerifyPayload("", `{}`)
require.ErrorIs(t, err, workos.ErrWebhookNotSigned)
}
func TestVerifyPayload_MalformedHeader(t *testing.T) {
verifier := workos.NewWebhookVerifier(testWebhookSecret)
_, err := verifier.VerifyPayload("garbage-header", `{}`)
require.ErrorIs(t, err, workos.ErrWebhookInvalidHeader)
}
func TestConstructEvent_ValidPayload(t *testing.T) {
body := `{"id":"event_01","object":"event","event":"user.created","created_at":"2024-01-01T00:00:00Z","data":{"id":"user_123"}}`
now := time.Now()
sigHeader := buildWebhookSigHeader(testWebhookSecret, body, now)
verifier := workos.NewWebhookVerifier(testWebhookSecret)
verifier.SetTolerance(5 * time.Minute)
event, err := verifier.ConstructEvent(sigHeader, body)
require.NoError(t, err)
require.Equal(t, "event_01", event.ID)
require.Equal(t, "event", event.Object)
require.Equal(t, "user.created", event.Event)
require.Equal(t, "user_123", event.Data["id"])
}
func TestComputeWebhookSignature(t *testing.T) {
sig := workos.ComputeWebhookSignature("secret", "1234567890", `{"hello":"world"}`)
require.NotEmpty(t, sig)
// Should be hex-encoded (64 chars for SHA-256).
require.Len(t, sig, 64)
// Deterministic: same inputs produce same output.
sig2 := workos.ComputeWebhookSignature("secret", "1234567890", `{"hello":"world"}`)
require.Equal(t, sig, sig2)
// Different secret should produce different signature.
sig3 := workos.ComputeWebhookSignature("other_secret", "1234567890", `{"hello":"world"}`)
require.NotEqual(t, sig, sig3)
}
func TestParseWebhookSignatureHeader(t *testing.T) {
ts, sig, err := workos.ParseWebhookSignatureHeader("t=1234567890, v1=abcdef0123456789")
require.NoError(t, err)
require.Equal(t, "1234567890", ts)
require.Equal(t, "abcdef0123456789", sig)
}
func TestParseWebhookSignatureHeader_Empty(t *testing.T) {
_, _, err := workos.ParseWebhookSignatureHeader("")
require.ErrorIs(t, err, workos.ErrWebhookNotSigned)
}
func TestParseWebhookSignatureHeader_MissingV1(t *testing.T) {
_, _, err := workos.ParseWebhookSignatureHeader("t=1234567890")
require.ErrorIs(t, err, workos.ErrWebhookInvalidHeader)
}
func TestParseWebhookSignatureHeader_MissingTimestamp(t *testing.T) {
_, _, err := workos.ParseWebhookSignatureHeader("v1=abcdef")
require.ErrorIs(t, err, workos.ErrWebhookInvalidHeader)
}