use std::sync::Arc;
use axum::extract::ws::{Message, WebSocket};
use objectiveai_sdk::cli::command::command_executor::websocket::AuthEnvelope;
use subtle::ConstantTimeEq;
pub(crate) async fn authenticate(socket: &mut WebSocket, secret: Option<&Arc<String>>) -> bool {
let text = loop {
match socket.recv().await {
Some(Ok(Message::Text(text))) => break text,
Some(Ok(Message::Close(_))) | Some(Err(_)) | None => return false,
Some(Ok(_)) => continue,
}
};
let Ok(envelope) = serde_json::from_str::<AuthEnvelope>(&text) else {
let _ = socket.send(Message::Close(None)).await;
return false;
};
if let Some(secret) = secret {
let verified = envelope
.signature
.as_deref()
.is_some_and(|signature| verify_signature(secret, signature));
if !verified {
let _ = socket.send(Message::Close(None)).await;
return false;
}
}
true
}
fn verify_signature(secret: &str, signature: &str) -> bool {
let Some(hex_sig) = signature.strip_prefix("sha256=") else {
return false;
};
let Ok(sig_bytes) = hex::decode(hex_sig) else {
return false;
};
use sha2::{Digest, Sha256};
let expected = Sha256::digest(secret.as_bytes());
expected.ct_eq(&sig_bytes).into()
}