Include payload in integrity controls
This commit is contained in:
@ -3,25 +3,34 @@ use crate::utils::curr_time;
|
||||
use actix_remote_ip::RemoteIP;
|
||||
use actix_web::dev::Payload;
|
||||
use actix_web::{FromRequest, HttpRequest};
|
||||
use bytes::Bytes;
|
||||
use jwt_simple::common::VerificationOptions;
|
||||
use jwt_simple::prelude::{Duration, HS256Key, MACLike};
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::net::IpAddr;
|
||||
use std::str::FromStr;
|
||||
|
||||
pub struct APIClientAuth {
|
||||
pub user: UserConfig,
|
||||
client: APIClient,
|
||||
payload: Option<Vec<u8>>,
|
||||
pub payload: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct TokenClaims {
|
||||
#[serde(rename = "met")]
|
||||
pub method: String,
|
||||
pub uri: String,
|
||||
#[serde(rename = "pay", skip_serializing_if = "Option::is_none")]
|
||||
pub payload_sha256: Option<String>,
|
||||
}
|
||||
|
||||
impl APIClientAuth {
|
||||
async fn extract_auth(req: &HttpRequest, remote_ip: IpAddr) -> Result<Self, actix_web::Error> {
|
||||
async fn extract_auth(
|
||||
req: &HttpRequest,
|
||||
remote_ip: IpAddr,
|
||||
payload_bytes: Option<Bytes>,
|
||||
) -> Result<Self, actix_web::Error> {
|
||||
let Some(token) = req.headers().get("x-client-auth") else {
|
||||
return Err(actix_web::error::ErrorBadRequest(
|
||||
"Missing authentication header!",
|
||||
@ -129,7 +138,27 @@ impl APIClientAuth {
|
||||
));
|
||||
}
|
||||
|
||||
// TODO : handle payload
|
||||
let payload = match (payload_bytes, claims.custom.payload_sha256) {
|
||||
(None, _) => None,
|
||||
(Some(_), None) => {
|
||||
return Err(actix_web::error::ErrorBadRequest(
|
||||
"A payload digest must be included in the JWT when the request has a payload!",
|
||||
));
|
||||
}
|
||||
(Some(payload), Some(provided_digest)) => {
|
||||
let computed_digest = base16ct::lower::encode_string(&Sha256::digest(&payload));
|
||||
if computed_digest != provided_digest {
|
||||
log::error!(
|
||||
"Expected digest {provided_digest} but computed {computed_digest}!"
|
||||
);
|
||||
return Err(actix_web::error::ErrorBadRequest(
|
||||
"Computed digest is different from the one provided in the JWT!",
|
||||
));
|
||||
}
|
||||
|
||||
Some(payload.to_vec())
|
||||
}
|
||||
};
|
||||
|
||||
// Update last use (if needed)
|
||||
if client.need_update_last_used() {
|
||||
@ -145,7 +174,7 @@ impl APIClientAuth {
|
||||
|
||||
Ok(Self {
|
||||
client: client.clone(),
|
||||
payload: None,
|
||||
payload,
|
||||
user,
|
||||
})
|
||||
}
|
||||
@ -163,6 +192,24 @@ impl FromRequest for APIClientAuth {
|
||||
Err(e) => return Box::pin(async { Err(e) }),
|
||||
};
|
||||
|
||||
Box::pin(async move { Self::extract_auth(&req, remote_ip.0).await })
|
||||
let mut payload = payload.take();
|
||||
|
||||
Box::pin(async move {
|
||||
let payload_bytes = match Bytes::from_request(&req, &mut payload).await {
|
||||
Ok(b) => {
|
||||
if b.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(b)
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Failed to extract request payload! {e}");
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
Self::extract_auth(&req, remote_ip.0, payload_bytes).await
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user