use oauth_device_flows::{DeviceFlow, DeviceFlowConfig, Provider};
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = DeviceFlowConfig::new()
.client_id("your-google-client-id.googleusercontent.com") .client_secret("your-google-client-secret") .scopes(vec![
"openid",
"email",
"profile",
"https://www.googleapis.com/auth/drive.readonly", ])
.poll_interval(Duration::from_secs(5))
.max_attempts(60);
let mut device_flow = DeviceFlow::new(Provider::Google, config)?;
println!("Starting Google OAuth Device Flow...");
let auth_response = device_flow.initialize().await?;
println!("\n🎯 Google Authentication Required");
println!("─────────────────────────────");
println!("1. Open this URL in your browser:");
println!(" {}", auth_response.verification_uri());
println!();
println!("2. Enter this code:");
println!(" {}", auth_response.user_code());
println!();
#[cfg(feature = "qr-codes")]
if let Ok(qr_code) = auth_response.generate_qr_code() {
println!("Or scan this QR code:");
println!("{}", qr_code);
println!();
}
println!("⏳ Waiting for you to complete authentication...");
println!(
" (This will timeout in {} seconds)",
auth_response.expires_in().as_secs()
);
println!();
match device_flow.poll_for_token().await {
Ok(token_response) => {
println!("✅ Authentication successful!");
println!(" Token type: {}", token_response.token_type);
println!(" Expires in: {:?}", token_response.expires_in);
println!(" Scopes: {:?}", token_response.scope);
println!(
" Has refresh token: {}",
token_response.refresh_token.is_some()
);
let mut token_manager = oauth_device_flows::TokenManager::new(
token_response,
Provider::Google,
DeviceFlowConfig::new()
.client_id("your-google-client-id.googleusercontent.com")
.client_secret("your-google-client-secret"),
)?;
println!("\n🚀 Making API call to Google...");
let client = reqwest::Client::new();
let user_response = client
.get("https://www.googleapis.com/oauth2/v2/userinfo")
.header("Authorization", token_manager.authorization_header())
.send()
.await?;
if user_response.status().is_success() {
let user_info: serde_json::Value = user_response.json().await?;
println!(
" User: {}",
user_info["name"].as_str().unwrap_or("Unknown")
);
println!(
" Email: {}",
user_info["email"].as_str().unwrap_or("Unknown")
);
println!(
" Picture: {}",
user_info["picture"].as_str().unwrap_or("None")
);
} else {
println!(" User API call failed: {}", user_response.status());
}
let drive_response = client
.get("https://www.googleapis.com/drive/v3/files?pageSize=10")
.header("Authorization", token_manager.authorization_header())
.send()
.await?;
if drive_response.status().is_success() {
let drive_data: serde_json::Value = drive_response.json().await?;
if let Some(files) = drive_data["files"].as_array() {
println!("\n📁 Recent Google Drive files:");
for file in files.iter().take(5) {
let name = file["name"].as_str().unwrap_or("Unknown");
let mime_type = file["mimeType"].as_str().unwrap_or("Unknown");
println!(" • {} ({})", name, mime_type);
}
}
} else {
println!(" Drive API call failed: {}", drive_response.status());
}
println!("\n🔄 Testing token refresh...");
match token_manager.refresh().await {
Ok(()) => {
println!(" Token refreshed successfully!");
println!(
" New token expires in: {:?}",
token_manager.remaining_lifetime()
);
}
Err(e) => {
println!(" Token refresh failed: {}", e);
}
}
}
Err(e) => {
eprintln!("❌ Authentication failed: {}", e);
match e {
oauth_device_flows::DeviceFlowError::AuthorizationDenied => {
eprintln!(" The user denied the authorization request.");
}
oauth_device_flows::DeviceFlowError::ExpiredToken => {
eprintln!(" The device code expired. Please try again.");
}
oauth_device_flows::DeviceFlowError::MaxAttemptsExceeded(max) => {
eprintln!(" Timed out after {} attempts. Please try again.", max);
}
_ => {
eprintln!(" Error details: {}", e);
}
}
}
}
Ok(())
}