use flowsdk::mqtt_client::client::{AuthResult, ConnectionResult};
use flowsdk::mqtt_client::tokio_async_client::{
TokioAsyncClientConfig, TokioAsyncMqttClient, TokioMqttEventHandler,
};
use flowsdk::mqtt_client::{MqttClientError, MqttClientOptions};
use flowsdk::mqtt_serde::mqttv5::common::properties::Property;
use std::sync::Arc;
use tokio::sync::Mutex;
#[derive(Clone)]
struct AuthExampleHandler {
client: Option<Arc<TokioAsyncMqttClient>>,
auth_step: Arc<Mutex<u32>>,
}
impl AuthExampleHandler {
fn new() -> Self {
Self {
client: None,
auth_step: Arc::new(Mutex::new(0)),
}
}
}
#[async_trait::async_trait]
impl TokioMqttEventHandler for AuthExampleHandler {
async fn on_connected(&mut self, result: &ConnectionResult) {
println!("✅ Connected to broker!");
println!(" Reason code: {}", result.reason_code);
println!(" Session present: {}", result.session_present);
}
async fn on_disconnected(&mut self, reason: Option<u8>) {
println!("❌ Disconnected from broker. Reason: {:?}", reason);
}
async fn on_auth_received(&mut self, result: &AuthResult) {
println!("\n🔐 AUTH packet received from broker:");
println!(
" Reason code: 0x{:02X} ({})",
result.reason_code,
result.reason_description()
);
let mut step = self.auth_step.lock().await;
*step += 1;
if result.is_continue() {
println!(" ➡️ Authentication continuing (step {})...", *step);
let mut auth_method = None;
let mut auth_data = None;
for prop in &result.properties {
match prop {
Property::AuthenticationMethod(method) => {
auth_method = Some(method.clone());
}
Property::AuthenticationData(data) => {
auth_data = Some(data.clone());
}
_ => {}
}
}
println!(" Authentication Method: {:?}", auth_method);
println!(
" Authentication Data length: {:?}",
auth_data.as_ref().map(|d| d.len())
);
if let Some(client) = &self.client {
let response_properties = vec![
Property::AuthenticationMethod(
auth_method.unwrap_or_else(|| "SCRAM-SHA-256".to_string()),
),
Property::AuthenticationData(vec![0x00, 0x01, 0x02, 0x03]), ];
println!(" 📤 Sending AUTH response...");
if let Err(e) = client.auth_continue(response_properties).await {
eprintln!(" ❌ Failed to send AUTH: {}", e);
}
}
} else if result.is_success() {
println!(" ✅ Authentication successful!");
} else if result.is_re_authenticate() {
println!(" 🔄 Re-authentication requested by broker");
if let Some(client) = &self.client {
let reauth_properties = vec![
Property::AuthenticationMethod("SCRAM-SHA-256".to_string()),
Property::AuthenticationData(vec![0x10, 0x20, 0x30, 0x40]),
];
println!(" 📤 Sending re-authentication...");
if let Err(e) = client.auth_re_authenticate(reauth_properties).await {
eprintln!(" ❌ Failed to send re-AUTH: {}", e);
}
}
}
}
async fn on_error(&mut self, error: &MqttClientError) {
eprintln!("⚠️ Error: {}", error.user_message());
}
async fn on_connection_lost(&mut self) {
println!("🔌 Connection lost");
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("🚀 MQTT v5 Enhanced Authentication Example");
println!("==========================================\n");
let options = MqttClientOptions::builder()
.peer("127.0.0.1:1883")
.client_id("auth_example_client")
.username("test_user")
.password(b"test_password".to_vec())
.keep_alive(60)
.build();
let config = TokioAsyncClientConfig::default();
let handler = AuthExampleHandler::new();
println!("📡 Creating MQTT client...");
let client = Arc::new(TokioAsyncMqttClient::new(options, Box::new(handler), config).await?);
println!("🔌 Connecting to broker...\n");
client.connect().await?;
println!("\n📤 Sending AUTH packet to broker (example)...");
let auth_properties = vec![
Property::AuthenticationMethod("SCRAM-SHA-256".to_string()),
Property::AuthenticationData(vec![0xAA, 0xBB, 0xCC, 0xDD]),
];
match client.auth(0x18, auth_properties).await {
Ok(_) => println!(" ✅ AUTH packet sent successfully"),
Err(e) => eprintln!(" ❌ Failed to send AUTH: {}", e),
}
println!("\n⏳ Running for 10 seconds to demonstrate AUTH flow...");
tokio::time::sleep(tokio::time::Duration::from_secs(10)).await;
println!("\n🛑 Disconnecting...");
client.disconnect().await?;
println!("✅ Example completed!\n");
tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
Ok(())
}