Skip to content

[TW-4803] feat(webhook): add Pub/Sub channels, secret rotation, and signature verification#46

Merged
qasim-nylas merged 2 commits intomainfrom
feature/TW-4803-pubsub-notifications-verify-rotate
Apr 8, 2026
Merged

[TW-4803] feat(webhook): add Pub/Sub channels, secret rotation, and signature verification#46
qasim-nylas merged 2 commits intomainfrom
feature/TW-4803-pubsub-notifications-verify-rotate

Conversation

@qasim-nylas
Copy link
Copy Markdown
Collaborator

@qasim-nylas qasim-nylas commented Apr 8, 2026

Summary

Adds Notifications 2.0 support to the Nylas CLI under the existing nylas webhook command group — Pub/Sub notification channel management, webhook secret rotation, and local signature verification, all backed by the Nylas v3 API.

JIRA: TW-4803 · Epic: TW-4638 (Nylas CLI)


New CLI Commands

Pub/Sub Notification Channels

Command Description
nylas webhook pubsub list List all Pub/Sub notification channels
nylas webhook pubsub show <id> Show channel details
nylas webhook pubsub create --topic <topic> --triggers <types> Create a channel
nylas webhook pubsub update <id> [flags] Update a channel
nylas webhook pubsub delete <id> --yes Delete a channel

Webhook Secret Rotation

Command Description
nylas webhook rotate-secret <id> --yes Rotate a webhook's signing secret

Local Signature Verification

Command Description
nylas webhook verify --secret <s> --signature <sig> --payload <body> Verify a webhook signature locally
nylas webhook verify --secret <s> --signature <sig> --payload-file <path> Verify using a payload file

What changed

1. Pub/Sub Notification Channels (nylas webhook pubsub)

  • Full CRUD for Google Cloud Pub/Sub notification channels
  • Topic validation enforces projects/<id>/topics/<id> format
  • Trigger types reuse existing domain.AllTriggerTypes() with validation
  • --topic and --triggers marked as required on create
  • Delete requires --yes confirmation
  • Supports --output json/yaml/quiet via shared output flags

2. Webhook Secret Rotation (nylas webhook rotate-secret)

  • Rotates webhook signing secret via the Nylas API
  • Requires --yes flag to prevent accidental rotation
  • Handles dual API response shapes defensively

3. Local Signature Verification (nylas webhook verify)

  • Verifies HMAC-SHA256 webhook signatures offline
  • Accepts --payload or --payload-file (mutually exclusive)
  • Uses shared verification logic extracted from the webhook server into webhookserver/signature.go
  • Same code path as the live webhook server's signature check

4. Shared Signature Module

  • Extracted ComputeSignature, VerifySignature, NormalizeSignature into internal/adapters/webhookserver/signature.go
  • Webhook server now delegates to this shared module
  • Empty secrets are defensively rejected
  • Signature normalization simplified (lowercase first, then strip prefix)

Architecture

Follows the existing hexagonal pattern across all layers:

Layer Pub/Sub Files Webhook Additions
Domain domain/pubsub.go RotateWebhookSecretResponse in domain/webhook.go
Ports ports/pubsub.go (PubSubClient interface) RotateWebhookSecret on WebhookClient
Adapters adapters/nylas/pubsub.go webhooks.go (rotate), webhookserver/signature.go (verify)
CLI cli/webhook/pubsub*.go (4 files) verify.go, rotate-secret in existing commands
Mock/Demo mock_pubsub.go, demo_pubsub.go mock_webhooks.go, demo_webhooks.go updated

Security

  • HMAC-SHA256 with hmac.Equal for constant-time comparison
  • VerifySignature rejects empty secrets defensively
  • Channel IDs are url.PathEscaped in API URLs
  • Nil request guards on create/update adapter methods
  • Demo mode returns proper not-found errors (no silent fallback)

Testing

  • Unit tests: Adapter CRUD (all 5 ops + edge cases), signature verification (8 table-driven cases: empty secret, binary payload, prefix variants, whitespace), CLI flag/structure tests, rotate-secret (4 cases), verify command (valid/invalid/missing flags/mutual exclusivity)
  • Integration tests: Split into webhooks_test.go (516 lines) + webhooks_notifications_test.go (94 lines) to stay under file size limits
  • Validation: make ci-full passes end-to-end

Test plan

  • go test ./internal/adapters/webhookserver -run 'TestVerifySignature|TestNormalizeSignature' -count=1
  • go test ./internal/adapters/nylas -run 'TestHTTPClient_.*PubSub|TestDemoClient_GetPubSubChannel_NotFound|TestHTTPClient_RotateWebhookSecret' -count=1
  • go test ./internal/cli/webhook -run 'TestPubSub|TestRotateSecretCommand|TestVerifyCommand' -count=1
  • go test ./internal/cli/integration -tags=integration -run 'TestCLI_Webhook(Help|CreateHelp|RotateSecretHelp|VerifyHelp|VerifyLocal|PubSubHelp|TestHelp|TestPayload)' -count=1
  • make ci-full

…ignature verification

Add Notifications 2.0 support under the existing webhook command group:
- Pub/Sub notification channel CRUD (list/show/create/update/delete)
- Webhook secret rotation with confirmation guard
- Local webhook signature verification using shared HMAC-SHA256 logic
  extracted from the webhook server into webhookserver/signature.go
rotate_secret.go was excluded by the *secret* gitignore pattern.
Add an exception for the legitimate source file so CI can find
the newRotateSecretCmd definition.
@qasim-nylas qasim-nylas requested a review from AaronDDM April 8, 2026 16:19
Copy link
Copy Markdown
Collaborator

@AaronDDM AaronDDM left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

@qasim-nylas qasim-nylas merged commit d9b6c1b into main Apr 8, 2026
6 checks passed
@qasim-nylas qasim-nylas deleted the feature/TW-4803-pubsub-notifications-verify-rotate branch April 8, 2026 16:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants