nestrs_webhooks/
verify.rs1use crate::sign::sign_standard_webhook;
2use subtle::ConstantTimeEq;
3
4#[derive(Debug, Clone)]
5pub enum WebhookVerifyError {
6 BadTimestamp,
7 BadSignature,
8}
9
10impl std::fmt::Display for WebhookVerifyError {
11 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
12 match self {
13 Self::BadTimestamp => write!(f, "invalid webhook timestamp"),
14 Self::BadSignature => write!(f, "invalid webhook signature"),
15 }
16 }
17}
18
19impl std::error::Error for WebhookVerifyError {}
20
21pub fn verify_standard_webhook(
22 secret: &str,
23 msg_id: &str,
24 timestamp: &str,
25 signature_header: &str,
26 body: &[u8],
27) -> Result<(), WebhookVerifyError> {
28 let ts: i64 = timestamp
29 .parse()
30 .map_err(|_| WebhookVerifyError::BadTimestamp)?;
31 let expected = sign_standard_webhook(secret, msg_id, ts, body);
32 let matched = signature_header.split_whitespace().any(|part| {
34 expected
35 .signature_header
36 .as_bytes()
37 .ct_eq(part.as_bytes())
38 .into()
39 });
40 if matched {
41 Ok(())
42 } else {
43 Err(WebhookVerifyError::BadSignature)
44 }
45}