kamu_snap_crypto_axum/
lib.rs1#![forbid(unsafe_code)]
14
15use http::request::Parts;
16use kamu_snap_crypto::Signature;
17use kamu_snap_crypto::snap_bi::{ServiceStringToSign, verify_service};
18
19const X_SIGNATURE: &str = "X-SIGNATURE";
20const X_TIMESTAMP: &str = "X-TIMESTAMP";
21const AUTHORIZATION: &str = "Authorization";
22
23pub fn verify_request(parts: &Parts, body: &[u8], client_secret: &str) -> kamu_snap_crypto::Result<()> {
30 let signature_b64 = header_str(&parts.headers, X_SIGNATURE)?;
31 let timestamp = header_str(&parts.headers, X_TIMESTAMP)?;
32 let authorization = header_str(&parts.headers, AUTHORIZATION)?;
33 let access_token = authorization.strip_prefix("Bearer ").unwrap_or(authorization);
34
35 let parts_canonical =
36 ServiceStringToSign { method: &parts.method, path: parts.uri.path(), access_token, body, timestamp };
37
38 let sig = Signature::from_base64(signature_b64)?;
39 verify_service(client_secret.as_bytes(), &parts_canonical, &sig)
40}
41
42fn header_str<'a>(headers: &'a http::HeaderMap, name: &'static str) -> kamu_snap_crypto::Result<&'a str> {
43 headers
44 .get(name)
45 .ok_or_else(|| kamu_snap_crypto::Error::Webhook(format!("missing header: {name}")))?
46 .to_str()
47 .map_err(|e| kamu_snap_crypto::Error::Webhook(format!("non-ASCII header {name}: {e}")))
48}