Skip to content

Update nango crate: add webhook module with verification, expand types per docs#3713

Merged
yujonglee merged 2 commits intomainfrom
devin/1770475555-update-nango-crate
Feb 7, 2026
Merged

Update nango crate: add webhook module with verification, expand types per docs#3713
yujonglee merged 2 commits intomainfrom
devin/1770475555-update-nango-crate

Conversation

@devin-ai-integration
Copy link
Contributor

@devin-ai-integration devin-ai-integration bot commented Feb 7, 2026

Summary

Updates crates/nango to align with the current Nango API docs for API auth and webhooks.

Changes:

  • types.rs: Added tags: Option<HashMap<String, String>> to NangoConnectSessionRequestUser per the connect session API. Removed the old minimal NangoConnectWebhook / NangoConnectWebhookEndUser types (unused outside this crate).
  • webhook.rs (new): Dedicated module containing:
    • verify_webhook_signature() — HMAC-SHA256 verification for the X-Nango-Hmac-Sha256 header
    • NangoAuthWebhook — full auth webhook payload (authMode, providerConfigKey, provider, environment, success, error, etc.)
    • NangoSyncWebhook / NangoSyncWebhookResults — sync webhook types
    • NangoWebhookEndUser — now includes endUserEmail and tags
    • NangoWebhookError — error payload for failed auth operations
    • Unit tests for signature verification and JSON deserialization of all webhook types

Review & Testing Checklist for Human

  • Timing side-channel in signature verification: verify_webhook_signature uses plain == string comparison rather than constant-time comparison (hmac::Mac::verify or similar). Evaluate whether this matters for your threat model.
  • Renamed webhook types: Old NangoConnectWebhookNangoAuthWebhook, NangoConnectWebhookEndUserNangoWebhookEndUser. Confirm no downstream consumers outside this crate reference the old names (grep showed none, but verify in any out-of-tree code).
  • Non-optional fields on NangoAuthWebhook: Fields like auth_mode, provider, environment are required. Verify against real Nango webhook payloads that these are always present.

Notes

  • hmac, sha2, hex added as direct crate deps (not workspace-level) since they're only used here.
  • Requested by: @yujonglee
  • Link to Devin run

Open with Devin

…s per docs

Co-Authored-By: yujonglee <yujonglee.dev@gmail.com>
@netlify
Copy link

netlify bot commented Feb 7, 2026

Deploy Preview for hyprnote canceled.

Name Link
🔨 Latest commit 4923c52
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote/deploys/698752a90e45350008feb472

@netlify
Copy link

netlify bot commented Feb 7, 2026

Deploy Preview for hyprnote-storybook canceled.

Name Link
🔨 Latest commit 4923c52
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote-storybook/deploys/698752a9f8d5ab0007c32954

@devin-ai-integration
Copy link
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Co-Authored-By: yujonglee <yujonglee.dev@gmail.com>
@yujonglee yujonglee merged commit 705950f into main Feb 7, 2026
15 of 16 checks passed
@yujonglee yujonglee deleted the devin/1770475555-update-nango-crate branch February 7, 2026 14:57
Copy link
Contributor Author

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 potential issue.

View 4 additional findings in Devin Review.

Open in Devin Review

Comment on lines +15 to +16
let expected = hex::encode(mac.finalize().into_bytes());
expected == signature
Copy link
Contributor Author

Choose a reason for hiding this comment

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

🔴 Timing side-channel in webhook signature verification using non-constant-time ==

The verify_webhook_signature function at crates/nango/src/webhook.rs:16 uses plain == string comparison to compare the computed HMAC against the provided signature. This is vulnerable to timing side-channel attacks, where an attacker can progressively determine the correct signature byte-by-byte by measuring response time differences.

Root Cause and Impact

Rust's == on strings short-circuits on the first differing byte. An attacker sending crafted webhook requests can measure how long the comparison takes and deduce how many leading bytes of their forged signature match the expected one. Over many requests, this allows reconstructing the full valid HMAC without knowing the secret key.

The hmac crate already provides Mac::verify() which performs constant-time comparison internally. The current code computes the HMAC correctly but then discards the constant-time verification by hex-encoding and using ==:

let expected = hex::encode(mac.finalize().into_bytes());
expected == signature

Instead, the signature should be decoded from hex and verified using mac.verify_slice(), or the raw bytes should be compared using a constant-time comparison function.

Impact: An attacker who can send arbitrary webhook payloads to the endpoint and measure response times could forge valid webhook signatures, bypassing authentication and injecting malicious webhook events.

Suggested change
let expected = hex::encode(mac.finalize().into_bytes());
expected == signature
let expected = mac.finalize().into_bytes();
let Ok(signature_bytes) = hex::decode(signature) else {
return false;
};
expected.as_slice().len() == signature_bytes.len()
&& hmac::Mac::verify_slice(HmacSha256::new_from_slice(secret_key.as_bytes()).unwrap(), &signature_bytes).is_ok()
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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.

1 participant