summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorClaudomator Agent <agent@claudomator>2026-03-17 02:12:31 +0000
committerClaudomator Agent <agent@claudomator>2026-03-17 02:12:31 +0000
commit94e45575a34e8672f8b405c54cabd8e524281fef (patch)
tree1bfdcb4883258ebd5e7a346edf4ab9d3adcffdba /internal
parent95ea6932e8cc7b49556498d6c896fe6f17270aa2 (diff)
fix: swap VAPID key return order in GenerateVAPIDKeys wrapper
webpush.GenerateVAPIDKeys() returns (privateKey, publicKey) but the claudomator wrapper declared (publicKey, privateKey), causing the 32-byte private key to be sent to browsers as the applicationServerKey. Browsers require a 65-byte uncompressed P256 point, so they rejected it with "The provided applicationServerKey is not valid." Adds a regression test that asserts public key is 87 chars/65 bytes with 0x04 prefix and private key is 43 chars/32 bytes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal')
-rw-r--r--internal/notify/vapid.go4
-rw-r--r--internal/notify/vapid_test.go43
2 files changed, 46 insertions, 1 deletions
diff --git a/internal/notify/vapid.go b/internal/notify/vapid.go
index 90d535b..d93a090 100644
--- a/internal/notify/vapid.go
+++ b/internal/notify/vapid.go
@@ -4,6 +4,8 @@ import webpush "github.com/SherClockHolmes/webpush-go"
// GenerateVAPIDKeys generates a VAPID key pair for web push notifications.
// Returns the base64url-encoded public and private keys.
+// Note: webpush.GenerateVAPIDKeys returns (privateKey, publicKey) — we swap here.
func GenerateVAPIDKeys() (publicKey, privateKey string, err error) {
- return webpush.GenerateVAPIDKeys()
+ privateKey, publicKey, err = webpush.GenerateVAPIDKeys()
+ return
}
diff --git a/internal/notify/vapid_test.go b/internal/notify/vapid_test.go
new file mode 100644
index 0000000..6157854
--- /dev/null
+++ b/internal/notify/vapid_test.go
@@ -0,0 +1,43 @@
+package notify
+
+import (
+ "encoding/base64"
+ "testing"
+)
+
+// TestGenerateVAPIDKeys_PublicKeyIs65Bytes verifies that the public key returned
+// by GenerateVAPIDKeys is a 65-byte uncompressed P256 EC point (base64url, no padding = 87 chars)
+// and the private key is 32 bytes (43 chars). Previously the return values were swapped.
+func TestGenerateVAPIDKeys_PublicKeyIs65Bytes(t *testing.T) {
+ pub, priv, err := GenerateVAPIDKeys()
+ if err != nil {
+ t.Fatalf("GenerateVAPIDKeys: %v", err)
+ }
+
+ // Public key: 65 bytes → 87 base64url chars (no padding).
+ if len(pub) != 87 {
+ t.Errorf("public key: want 87 chars (65 bytes), got %d chars (%q)", len(pub), pub)
+ }
+ pubBytes, err := base64.RawURLEncoding.DecodeString(pub)
+ if err != nil {
+ t.Fatalf("public key base64url decode: %v", err)
+ }
+ if len(pubBytes) != 65 {
+ t.Errorf("public key bytes: want 65, got %d", len(pubBytes))
+ }
+ if pubBytes[0] != 0x04 {
+ t.Errorf("public key first byte: want 0x04 (uncompressed point), got 0x%02x", pubBytes[0])
+ }
+
+ // Private key: 32 bytes → 43 base64url chars (no padding).
+ if len(priv) != 43 {
+ t.Errorf("private key: want 43 chars (32 bytes), got %d chars (%q)", len(priv), priv)
+ }
+ privBytes, err := base64.RawURLEncoding.DecodeString(priv)
+ if err != nil {
+ t.Fatalf("private key base64url decode: %v", err)
+ }
+ if len(privBytes) != 32 {
+ t.Errorf("private key bytes: want 32, got %d", len(privBytes))
+ }
+}