oauth-device-flows 0.1.0

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

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 GitHub
    let config = DeviceFlowConfig::new()
        .client_id("your-github-client-id") // Replace with your GitHub app client ID
        .scopes(vec![
            "user:email", // Access user email
            "repo",       // Access repositories
            "read:org",   // Read organization membership
        ])
        .poll_interval(Duration::from_secs(5))
        .max_attempts(60);

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

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

    // Display instructions to the user
    println!("\n🐙 GitHub 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!("   Scopes: {:?}", token_response.scope);

            // Create a token manager
            let token_manager = oauth_device_flows::TokenManager::new(
                token_response,
                Provider::GitHub,
                DeviceFlowConfig::new().client_id("your-github-client-id"),
            )?;

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

            // Get user information
            let user_response = client
                .get("https://api.github.com/user")
                .header("Authorization", token_manager.authorization_header())
                .header("User-Agent", "oauth-device-flows-example")
                .send()
                .await?;

            if user_response.status().is_success() {
                let user_info: serde_json::Value = user_response.json().await?;
                println!(
                    "   User: {}",
                    user_info["login"].as_str().unwrap_or("Unknown")
                );
                println!(
                    "   Name: {}",
                    user_info["name"].as_str().unwrap_or("Unknown")
                );
                println!(
                    "   Public repos: {}",
                    user_info["public_repos"].as_u64().unwrap_or(0)
                );
            } else {
                println!("   User API call failed: {}", user_response.status());
            }

            // Get repositories
            let repos_response = client
                .get("https://api.github.com/user/repos?per_page=5&sort=updated")
                .header("Authorization", token_manager.authorization_header())
                .header("User-Agent", "oauth-device-flows-example")
                .send()
                .await?;

            if repos_response.status().is_success() {
                let repos: serde_json::Value = repos_response.json().await?;
                if let Some(repos_array) = repos.as_array() {
                    println!("\n📚 Recent repositories:");
                    for repo in repos_array.iter().take(5) {
                        let name = repo["name"].as_str().unwrap_or("Unknown");
                        let language = repo["language"].as_str().unwrap_or("Unknown");
                        let updated = repo["updated_at"].as_str().unwrap_or("Unknown");
                        println!("{} ({}) - last updated: {}", name, language, updated);
                    }
                }
            } else {
                println!("   Repos API call failed: {}", repos_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(())
}