oauth-device-flows 0.1.0

A specialized Rust library implementing OAuth 2.0 Device Authorization Grant (RFC 8628)
Documentation
//! Google OAuth Device Flow Example
//!
//! This example demonstrates how to use the oauth-device-flows library
//! to authenticate with Google.

use oauth_device_flows::{DeviceFlow, DeviceFlowConfig, Provider};
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Configure the device flow for Google
    // Note: Google requires a client_secret for device flows
    let config = DeviceFlowConfig::new()
        .client_id("your-google-client-id.googleusercontent.com") // Replace with your Google client ID
        .client_secret("your-google-client-secret") // Replace with your Google client secret
        .scopes(vec![
            "openid",
            "email",
            "profile",
            "https://www.googleapis.com/auth/drive.readonly", // Example: read-only access to Google Drive
        ])
        .poll_interval(Duration::from_secs(5))
        .max_attempts(60);

    // Create a device flow instance
    let mut device_flow = DeviceFlow::new(Provider::Google, config)?;

    // Start the device authorization flow
    println!("Starting Google OAuth Device Flow...");
    let auth_response = device_flow.initialize().await?;

    // Display instructions to the user
    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!();

    // Show QR code if available
    #[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!();

    // Poll for the token
    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()
            );

            // Create a token manager
            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"),
            )?;

            // Example: Use the token to make Google API calls
            println!("\n🚀 Making API call to Google...");
            let client = reqwest::Client::new();

            // Get user information
            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());
            }

            // Example: List Google Drive files (if drive scope was granted)
            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());
            }

            // Example: Demonstrate token refresh
            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(())
}