oauth-device-flows 0.1.0

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

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 Microsoft
    let config = DeviceFlowConfig::new()
        .client_id("your-client-id-here") // Replace with your actual client ID
        .scopes(vec![
            "https://graph.microsoft.com/User.Read",
            "https://graph.microsoft.com/Files.Read",
            "offline_access", // For refresh token
        ])
        .poll_interval(Duration::from_secs(5))
        .max_attempts(60); // 5 minutes total

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

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

    // Display instructions to the user
    println!("\n🔐 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 for easy token management
            let token_manager = oauth_device_flows::TokenManager::new(
                token_response,
                Provider::Microsoft,
                DeviceFlowConfig::new().client_id("your-client-id-here"),
            )?;

            // Example: Use the token to make an API call
            println!("\n🚀 Making API call to Microsoft Graph...");
            let client = reqwest::Client::new();
            let response = client
                .get("https://graph.microsoft.com/v1.0/me")
                .header("Authorization", token_manager.authorization_header())
                .send()
                .await?;

            if response.status().is_success() {
                let user_info: serde_json::Value = response.json().await?;
                println!(
                    "   User: {}",
                    user_info["displayName"].as_str().unwrap_or("Unknown")
                );
                println!(
                    "   Email: {}",
                    user_info["mail"].as_str().unwrap_or("Unknown")
                );
            } else {
                println!("   API call failed: {}", response.status());
            }
        }
        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(())
}