use futures_util::StreamExt;
use signer_core::{SignerKeys, SignerUser};
use signer_remote::auth::SignerRemoteAuth;
use std::time::Duration;
use tokio::sync::mpsc;
#[tokio::test]
async fn test_e2e_auth_flow() {
let base_url = "http://localhost:8080";
let (code_tx, mut code_rx) = mpsc::channel::<String>(1);
let (jwt_tx, mut jwt_rx) = mpsc::channel::<String>(1);
let client_handle = tokio::spawn(async move {
let client = reqwest::Client::new();
let mut stream = client
.get(format!( "{}/api/auth/new", base_url))
.send()
.await
.unwrap()
.bytes_stream();
let mut code_received = false;
let mut buffer = String::new();
while let Some(item) = stream.next().await {
let chunk = item.unwrap();
let chunk_str = String::from_utf8(chunk.to_vec()).unwrap();
buffer.push_str(&chunk_str);
while let Some(event_end) = buffer.find("\n\n") {
let event = buffer[..event_end].to_string();
buffer = buffer[event_end + 2..].to_string();
for line in event.lines() {
if line.starts_with("data:") {
let data = line.trim_start_matches("data:").trim();
if let Ok(auth_code) = serde_json::from_str::<serde_json::Value>(data) {
if !code_received {
if let (Some(code), Some(status)) = (
auth_code.get("code").and_then(|c| c.as_str()),
auth_code.get("status").and_then(|s| s.as_str()),
) {
if status == "Pending" {
code_tx.send(code.to_string()).await.unwrap();
code_received = true;
}
}
} else {
if let Some(status) = auth_code.get("status").and_then(|s| s.as_str()) {
if status == "Completed" {
if let Some(received_jwt) = auth_code.get("jwt").and_then(|j| j.as_str()) {
let expected_jwt = jwt_rx.recv().await.unwrap();
assert_eq!(received_jwt, expected_jwt);
return;
}
}
}
}
}
}
}
}
}
});
let auth_handle = tokio::spawn(async move {
if let Some(code) = code_rx.recv().await {
let auth = SignerRemoteAuth::from_url(base_url.to_string());
let detail_url = format!( "{}/api/auth/{}", base_url, code);
let detail = auth.get_auth_detail(&detail_url).await.unwrap();
let keys = SignerKeys::generate().unwrap();
let user = SignerUser {
pub_key: keys.pub_key.clone(),
username: "tester".to_string(),
..Default::default()
};
let claims = signer_auth::SignerJWTClaims::default(&keys, &user, uuid::Uuid::new_v4().to_string(), detail.state.clone())
.with_expired_duration(chrono::Duration::milliseconds(3600));
let jwt = signer_auth::SignerJWT::new(signer_auth::SignerJWTHeader::default(&user), claims)
.encode(&keys)
.expect("encode jwt string failed");
jwt_tx.send(jwt.clone()).await.unwrap();
let target_url = format!( "{}/api/auth/{}", base_url, code);
let result = auth.post_auth(target_url, detail.state, jwt).await;
assert!(result.is_ok());
}
});
let timeout = Duration::from_secs(10);
tokio::time::timeout(timeout, async {
let (client_res, auth_res) = tokio::join!(client_handle, auth_handle);
client_res.unwrap();
auth_res.unwrap();
})
.await
.expect("Test timed out!");
}