diff --git a/docs/.agent/docs_coverage.md b/docs/.agent/docs_coverage.md index 72f5425..424ed27 100644 --- a/docs/.agent/docs_coverage.md +++ b/docs/.agent/docs_coverage.md @@ -12,8 +12,11 @@ | Links | `recipes/pagination.md` | `rustapi-core/src/hateoas.rs` | OK | | **Extras** | | | | | Auth (JWT) | `recipes/jwt_auth.md` | `rustapi-extras/src/jwt` | OK | +| Auth (OAuth2) | `recipes/oauth2_client.md` | `rustapi-extras/src/oauth2` | OK | | Security | `recipes/csrf_protection.md` | `rustapi-extras/src/security` | OK | | Observability | `crates/rustapi_extras.md` | `rustapi-extras/src/telemetry` | OK | +| Audit Logging | `recipes/audit_logging.md` | `rustapi-extras/src/audit` | OK | +| Middleware (Advanced) | `recipes/advanced_middleware.md` | `rustapi-extras/src/{rate_limit, dedup, cache}` | OK | | **Jobs** | | | | | Job Queue (Crate) | `crates/rustapi_jobs.md` | `rustapi-jobs` | OK | | Background Jobs (Recipe) | `recipes/background_jobs.md` | `rustapi-jobs` | OK | diff --git a/docs/.agent/docs_inventory.md b/docs/.agent/docs_inventory.md index e040307..8340a53 100644 --- a/docs/.agent/docs_inventory.md +++ b/docs/.agent/docs_inventory.md @@ -8,5 +8,8 @@ | `docs/cookbook/src/getting_started/installation.md` | Installation instructions | Docs | OK | | `docs/cookbook/src/learning/README.md` | Learning path entry point | Docs | OK | | `docs/cookbook/src/recipes/*.md` | Specific implementation guides | Docs | OK | +| `docs/cookbook/src/recipes/advanced_middleware.md` | Recipe for Rate Limit, Dedup, Cache | Docs | OK | +| `docs/cookbook/src/recipes/audit_logging.md` | Recipe for Audit Logging | Docs | OK | +| `docs/cookbook/src/recipes/oauth2_client.md` | Recipe for OAuth2 Client | Docs | OK | | `crates/rustapi-core/src/hateoas.rs` | API Reference for HATEOAS | rustapi-core | OK | | `crates/rustapi-core/src/extract.rs` | API Reference for Extractors | rustapi-core | OK | diff --git a/docs/.agent/last_run.json b/docs/.agent/last_run.json index 6580305..5ca8a9a 100644 --- a/docs/.agent/last_run.json +++ b/docs/.agent/last_run.json @@ -1,5 +1,5 @@ { "last_processed_ref": "v0.1.335", - "date": "2026-02-15", - "notes": "Fixed critical inaccuracy in SSR recipe. Updated recipes index to be comprehensive. Verified docs coverage." + "date": "2026-02-16", + "notes": "Added recipes for Advanced Middleware, Audit Logging, and OAuth2 Client. Updated Learning Path to include these features." } diff --git a/docs/.agent/run_report_2026-02-16.md b/docs/.agent/run_report_2026-02-16.md new file mode 100644 index 0000000..507cbda --- /dev/null +++ b/docs/.agent/run_report_2026-02-16.md @@ -0,0 +1,30 @@ +# Run Report: 2026-02-16 + +## Detected Version +- **Ref:** v0.1.335 +- **Status:** No version change detected. Running "Continuous Improvement" phase. + +## Changes +### Cookbook Improvements +- **New Recipe:** `recipes/advanced_middleware.md` + - Covers Rate Limiting (`RateLimitLayer`), Request Deduplication (`DedupLayer`), and Response Caching (`CacheLayer`). +- **New Recipe:** `recipes/audit_logging.md` + - Covers Audit Events (`AuditEvent`), Actions (`AuditAction`), Compliance (`ComplianceInfo`), and GDPR/SOC2 features. +- **New Recipe:** `recipes/oauth2_client.md` + - Covers OAuth2 Client configuration (`OAuth2Config`) and authorization flow (`OAuth2Client`). + +### Learning Path Improvements +- **Curriculum Update:** `learning/curriculum.md` + - Added **Module 8: Advanced Middleware** (Phase 3). + - Added **Module 7: Authentication** expanded to include OAuth2. + - Added **Module 12: Observability & Auditing** (Phase 4). + - Renumbered subsequent modules to maintain sequence. + +### Documentation Tracking +- Updated `docs_inventory.md` with new recipe files. +- Updated `docs_coverage.md` to reflect coverage of `rate-limit`, `dedup`, `cache`, `audit`, and `oauth2-client` features in `rustapi-extras`. + +## TODOs / Next Steps +- Validate links in new recipes. +- Consider adding a dedicated recipe for "Guard" (RBAC) middleware once the feature stabilizes. +- Verify `rustapi-extras` feature flags in examples against `Cargo.toml`. diff --git a/docs/cookbook/src/SUMMARY.md b/docs/cookbook/src/SUMMARY.md index 0909abe..bab3611 100644 --- a/docs/cookbook/src/SUMMARY.md +++ b/docs/cookbook/src/SUMMARY.md @@ -31,17 +31,20 @@ - [Creating Resources](recipes/crud_resource.md) - [Pagination & HATEOAS](recipes/pagination.md) - [JWT Authentication](recipes/jwt_auth.md) + - [OAuth2 Client](recipes/oauth2_client.md) - [CSRF Protection](recipes/csrf_protection.md) - [Database Integration](recipes/db_integration.md) - [Testing & Mocking](recipes/testing.md) - [File Uploads](recipes/file_uploads.md) - [Background Jobs](recipes/background_jobs.md) - [Custom Middleware](recipes/custom_middleware.md) + - [Advanced Middleware](recipes/advanced_middleware.md) - [Real-time Chat](recipes/websockets.md) - [Server-Side Rendering (SSR)](recipes/server_side_rendering.md) - [AI Integration (TOON)](recipes/ai_integration.md) - [Production Tuning](recipes/high_performance.md) - [Resilience Patterns](recipes/resilience.md) + - [Audit Logging](recipes/audit_logging.md) - [Time-Travel Debugging (Replay)](recipes/replay.md) - [Deployment](recipes/deployment.md) - [HTTP/3 (QUIC)](recipes/http3_quic.md) diff --git a/docs/cookbook/src/learning/curriculum.md b/docs/cookbook/src/learning/curriculum.md index ff8255c..530ac14 100644 --- a/docs/cookbook/src/learning/curriculum.md +++ b/docs/cookbook/src/learning/curriculum.md @@ -107,19 +107,37 @@ This curriculum is designed to take you from a RustAPI beginner to an advanced u **Goal:** Security, Real-time, and Production readiness. -### Module 7: Authentication (JWT) +### Module 7: Authentication (JWT & OAuth2) - **Prerequisites:** Phase 2. -- **Reading:** [JWT Auth Recipe](../recipes/jwt_auth.md). -- **Task:** Implement a login route that returns a JWT. Protect user routes with `AuthUser` extractor. +- **Reading:** [JWT Auth Recipe](../recipes/jwt_auth.md), [OAuth2 Client](../recipes/oauth2_client.md). +- **Task:** + 1. Implement a login route that returns a JWT. + 2. Protect user routes with `AuthUser` extractor. + 3. (Optional) Implement "Login with Google" using `OAuth2Client`. - **Expected Output:** Protected routes return `401 Unauthorized` without a valid token. - **Pitfalls:** Hardcoding secrets. Not checking token expiration. #### 🧠 Knowledge Check 1. What is the role of the `AuthUser` extractor? -2. How do you protect a route with JWT? +2. How does OAuth2 PKCE improve security? 3. Where should you store the JWT secret? -### Module 8: WebSockets & Real-time +### Module 8: Advanced Middleware +- **Prerequisites:** Module 7. +- **Reading:** [Advanced Middleware](../recipes/advanced_middleware.md). +- **Task:** + 1. Apply `RateLimitLayer` to your login endpoint (10 requests/minute). + 2. Add `DedupLayer` to a payment endpoint. + 3. Cache the response of a public "stats" endpoint. +- **Expected Output:** Sending 11 login attempts results in `429 Too Many Requests`. +- **Pitfalls:** Caching responses that contain user-specific data. + +#### 🧠 Knowledge Check +1. What header indicates when the rate limit resets? +2. Why is request deduplication important for payments? +3. Which requests are typically safe to cache? + +### Module 9: WebSockets & Real-time - **Prerequisites:** Phase 2. - **Reading:** [WebSockets Recipe](../recipes/websockets.md). - **Task:** Create a chat endpoint where users can broadcast messages. @@ -131,21 +149,21 @@ This curriculum is designed to take you from a RustAPI beginner to an advanced u 2. Can you share state between HTTP handlers and WebSocket handlers? 3. What happens if a WebSocket handler panics? -### Module 9: Production Readiness & Deployment +### Module 10: Production Readiness & Deployment - **Prerequisites:** Phase 3. - **Reading:** [Production Tuning](../recipes/high_performance.md), [Resilience](../recipes/resilience.md), [Deployment](../recipes/deployment.md). - **Task:** - 1. Add `RateLimitLayer`, `CompressionLayer`, and `TimeoutLayer`. + 1. Add `CompressionLayer`, and `TimeoutLayer`. 2. Use `cargo rustapi deploy docker` to generate a Dockerfile. - **Expected Output:** A resilient API ready for deployment. - **Pitfalls:** Setting timeouts too low for slow operations. #### 🧠 Knowledge Check -1. Why is rate limiting important? +1. Why is timeout middleware important? 2. What command generates a production Dockerfile? 3. How do you enable compression for responses? -### Module 10: Background Jobs & Testing +### Module 11: Background Jobs & Testing - **Prerequisites:** Phase 3. - **Reading:** [Background Jobs Recipe](../recipes/background_jobs.md), [Testing Strategy](../concepts/testing.md). - **Task:** @@ -162,7 +180,7 @@ This curriculum is designed to take you from a RustAPI beginner to an advanced u ### 🏆 Phase 3 Capstone: "The Real-Time Collaboration Tool" **Objective:** Build a real-time collaborative note-taking app. **Requirements:** -- **Auth:** Users must log in to edit notes. +- **Auth:** Users must log in (JWT or OAuth2) to edit notes. - **Real-time:** Changes to a note are broadcast to all viewers via WebSockets. - **Jobs:** When a note is deleted, schedule a background job to archive it (simulate archive). - **Resilience:** Rate limit API requests to prevent abuse. @@ -174,22 +192,22 @@ This curriculum is designed to take you from a RustAPI beginner to an advanced u **Goal:** Build observable, resilient, and high-performance distributed systems. -### Module 11: Observability +### Module 12: Observability & Auditing - **Prerequisites:** Phase 3. -- **Reading:** [Observability (Extras)](../crates/rustapi_extras.md#observability), [Structured Logging](../crates/rustapi_extras.md#structured-logging). +- **Reading:** [Observability (Extras)](../crates/rustapi_extras.md#observability), [Audit Logging](../recipes/audit_logging.md). - **Task:** - 1. Enable `structured-logging` and `otel` features. - 2. Configure tracing to export spans to Jaeger (or console for dev). - 3. Add custom metrics for "active_users" and "jobs_processed". -- **Expected Output:** Logs are JSON formatted with trace IDs. Metrics endpoint exposes Prometheus data. -- **Pitfalls:** High cardinality in metric labels (e.g., using user IDs as labels). + 1. Enable `structured-logging` and `otel`. + 2. Configure tracing to export spans. + 3. Implement `AuditStore` and log a "User Login" event with IP address. +- **Expected Output:** Logs are JSON formatted. Audit log contains a new entry for every login. +- **Pitfalls:** High cardinality in metric labels. #### 🧠 Knowledge Check -1. What is the difference between logging and tracing? -2. How do you correlate logs across microservices? -3. What is the standard format for structured logs in RustAPI? +1. What is the difference between logging and auditing? +2. Which fields are required in an `AuditEvent`? +3. How does structured logging aid debugging? -### Module 12: Resilience & Security +### Module 13: Resilience & Security - **Prerequisites:** Phase 3. - **Reading:** [Resilience Patterns](../recipes/resilience.md), [Time-Travel Debugging](../recipes/replay.md). - **Task:** @@ -204,7 +222,7 @@ This curriculum is designed to take you from a RustAPI beginner to an advanced u 2. Why is jitter important in retry strategies? 3. How does Time-Travel Debugging help with "Heisenbugs"? -### Module 13: High Performance +### Module 14: High Performance - **Prerequisites:** Phase 3. - **Reading:** [HTTP/3 (QUIC)](../recipes/http3_quic.md), [Performance Tuning](../recipes/high_performance.md). - **Task:** @@ -226,6 +244,7 @@ This curriculum is designed to take you from a RustAPI beginner to an advanced u - **Processing:** Push events to a `rustapi-jobs` queue (Redis backend). - **Storage:** Workers process events and store aggregates in a database. - **Observability:** Full tracing from ingestion to storage. +- **Audit:** Log all configuration changes to the system. - **Resilience:** Circuit breakers on database writes. - **Testing:** Load test the ingestion endpoint (e.g., with k6 or similar) and observe metrics. @@ -235,7 +254,7 @@ This curriculum is designed to take you from a RustAPI beginner to an advanced u **Goal:** Master integration with AI, gRPC, and server-side rendering. -### Module 14: Server-Side Rendering (SSR) +### Module 15: Server-Side Rendering (SSR) - **Prerequisites:** Phase 2. - **Reading:** [SSR Recipe](../recipes/server_side_rendering.md). - **Task:** Create a dashboard showing system status using `rustapi-view`. @@ -247,7 +266,7 @@ This curriculum is designed to take you from a RustAPI beginner to an advanced u 2. How do you pass data to a template? 3. How does template reloading work in debug mode? -### Module 15: gRPC Microservices +### Module 16: gRPC Microservices - **Prerequisites:** Phase 3. - **Reading:** [gRPC Recipe](../recipes/grpc_integration.md). - **Task:** Run a gRPC service alongside your HTTP API that handles internal user lookups. @@ -259,7 +278,7 @@ This curriculum is designed to take you from a RustAPI beginner to an advanced u 2. Can HTTP and gRPC share the same Tokio runtime? 3. Why might you want to run both in the same process? -### Module 16: AI Integration (TOON) +### Module 17: AI Integration (TOON) - **Prerequisites:** Phase 2. - **Reading:** [AI Integration Recipe](../recipes/ai_integration.md). - **Task:** Create an endpoint that returns standard JSON for browsers but TOON for `Accept: application/toon`. diff --git a/docs/cookbook/src/recipes/advanced_middleware.md b/docs/cookbook/src/recipes/advanced_middleware.md new file mode 100644 index 0000000..d966197 --- /dev/null +++ b/docs/cookbook/src/recipes/advanced_middleware.md @@ -0,0 +1,125 @@ +# Advanced Middleware: Rate Limiting, Caching, and Deduplication + +As your API grows, you'll need to protect it from abuse and optimize performance. RustAPI provides a suite of advanced middleware in `rustapi-extras` to handle these concerns efficiently. + +These patterns are essential for the "Enterprise Platform" learning path and high-traffic services. + +## Prerequisites + +Add the `rustapi-extras` crate with the necessary features to your `Cargo.toml`. + +```toml +[dependencies] +rustapi-rs = { version = "0.1.335", features = ["full"] } +# OR cherry-pick features +# rustapi-extras = { version = "0.1.335", features = ["rate-limit", "dedup", "cache"] } +``` + +## Rate Limiting + +Rate limiting protects your API from being overwhelmed by too many requests from a single client. It uses a "Token Bucket" or "Fixed Window" algorithm to enforce limits. + +### How it works +The `RateLimitLayer` tracks request counts per IP address. When a limit is exceeded, it returns `429 Too Many Requests` with a `Retry-After` header. + +### Usage + +```rust +use rustapi_rs::prelude::*; +use rustapi_extras::rate_limit::RateLimitLayer; +use std::time::Duration; + +fn main() { + let app = RustApi::new() + .layer( + RateLimitLayer::new(100, Duration::from_secs(60)) // 100 requests per minute + ) + .route("/", get(handler)); + + // ... run app +} +``` + +The middleware automatically adds standard headers to responses: +- `X-RateLimit-Limit`: The maximum number of requests allowed. +- `X-RateLimit-Remaining`: The number of requests remaining in the current window. +- `X-RateLimit-Reset`: The timestamp when the window resets. + +## Request Deduplication + +In distributed systems, clients may retry requests that have already been processed (e.g., due to network timeouts). Deduplication ensures that non-idempotent operations (like payments) are processed only once. + +### How it works +The `DedupLayer` checks for an `Idempotency-Key` header. If a request with the same key is seen within the TTL window, it returns `409 Conflict`. + +### Usage + +```rust +use rustapi_rs::prelude::*; +use rustapi_extras::dedup::DedupLayer; +use std::time::Duration; + +fn main() { + let app = RustApi::new() + .layer( + DedupLayer::new() + .header_name("X-Idempotency-Key") // Optional: Custom header name + .ttl(Duration::from_secs(300)) // 5 minutes TTL + ) + .route("/payments", post(payment_handler)); + + // ... run app +} +``` + +Clients should generate a unique UUID for each operation and send it in the `Idempotency-Key` header. + +## Response Caching + +Caching can significantly reduce load on your servers by serving stored responses for identical requests. + +### How it works +The `CacheLayer` stores successful responses in memory based on the request method and URI. Subsequent requests are served from the cache until the TTL expires. + +### Usage + +```rust +use rustapi_rs::prelude::*; +use rustapi_extras::cache::CacheLayer; +use std::time::Duration; + +fn main() { + let app = RustApi::new() + .layer( + CacheLayer::new() + .ttl(Duration::from_secs(60)) // Cache for 60 seconds + .add_method("GET") // Cache GET requests + .add_method("HEAD") // Cache HEAD requests + ) + .route("/heavy-computation", get(heavy_handler)); + + // ... run app +} +``` + +Cached responses include an `X-Cache: HIT` header. Original responses have `X-Cache: MISS`. + +## Combining Middleware + +You can combine these layers to create a robust defense-in-depth strategy. + +```rust +let app = RustApi::new() + // 1. Rate Limit (Outer): Reject excessive traffic first + .layer(RateLimitLayer::new(1000, Duration::from_secs(60))) + + // 2. Deduplication: Prevent double-processing + .layer(DedupLayer::new()) + + // 3. Cache: Serve static/computed content quickly + .layer(CacheLayer::new().ttl(Duration::from_secs(30))) + + .route("/", get(handler)); +``` + +> **Note**: Order matters! Placing Rate Limit first saves resources by rejecting requests before they hit the cache or application logic. diff --git a/docs/cookbook/src/recipes/audit_logging.md b/docs/cookbook/src/recipes/audit_logging.md new file mode 100644 index 0000000..dde6135 --- /dev/null +++ b/docs/cookbook/src/recipes/audit_logging.md @@ -0,0 +1,105 @@ +# Audit Logging & Compliance + +In many enterprise applications, maintaining a detailed audit trail is crucial for security, compliance (GDPR, SOC2), and troubleshooting. RustAPI provides a comprehensive audit logging system in `rustapi-extras`. + +This recipe covers how to create, log, and query audit events. + +## Prerequisites + +Add `rustapi-extras` with the `audit` feature to your `Cargo.toml`. + +```toml +[dependencies] +rustapi-extras = { version = "0.1.335", features = ["audit"] } +``` + +## Core Concepts + +The audit system is built around three main components: +- **AuditEvent**: Represents a single action performed by a user or system. +- **AuditStore**: Interface for persisting events (e.g., `InMemoryAuditStore`, `FileAuditStore`). +- **ComplianceInfo**: Additional metadata for regulatory requirements. + +## Basic Usage + +Log a simple event when a user is created. + +```rust +use rustapi_extras::audit::{AuditEvent, AuditAction, InMemoryAuditStore, AuditStore}; + +#[tokio::main] +async fn main() { + // Initialize the store (could be FileAuditStore for persistence) + let store = InMemoryAuditStore::new(); + + // Create an event + let event = AuditEvent::new(AuditAction::Create) + .resource("users", "user-123") // Resource type & ID + .actor("admin@example.com") // Who performed the action + .ip_address("192.168.1.1".parse().unwrap()) + .success(true); // Outcome + + // Log it asynchronously + store.log(event); + + // ... later, query events + let recent_logs = store.query().limit(10).execute().await; + println!("Recent logs: {:?}", recent_logs); +} +``` + +## Compliance Features (GDPR & SOC2) + +RustAPI's audit system includes dedicated fields for compliance tracking. + +### GDPR Relevance +Events involving personal data can be flagged with legal basis and retention policies. + +```rust +use rustapi_extras::audit::{ComplianceInfo, AuditEvent, AuditAction}; + +let compliance = ComplianceInfo::new() + .personal_data(true) // Involves PII + .data_subject("user-123") // The person the data belongs to + .legal_basis("consent") // Article 6 basis + .retention("30_days"); // Retention policy + +let event = AuditEvent::new(AuditAction::Update) + .compliance(compliance) + .resource("profile", "user-123"); +``` + +### SOC2 Controls +Link events to specific security controls. + +```rust +let compliance = ComplianceInfo::new() + .soc2_control("CC6.1"); // Access Control + +let event = AuditEvent::new(AuditAction::Login) + .compliance(compliance) + .actor("employee@company.com"); +``` + +## Tracking Changes + +For updates, it's often useful to record what changed. + +```rust +use rustapi_extras::audit::AuditChanges; + +let changes = AuditChanges::new() + .field("email", "old@example.com", "new@example.com") + .field("role", "user", "admin"); + +let event = AuditEvent::new(AuditAction::Update) + .changes(changes) + .resource("users", "user-123"); +``` + +## Best Practices + +1. **Log All Security Events**: Logins (success/failure), permission changes, and API key management should always be audited. +2. **Include Context**: Add `request_id` or `session_id` to correlate logs with tracing data. +3. **Use Asynchronous Logging**: The `AuditStore` is designed to be non-blocking. Use it in a background task or `tokio::spawn` if needed for heavy writes. +4. **Secure the Logs**: Ensure that the storage backend (file, database) is protected from tampering. diff --git a/docs/cookbook/src/recipes/oauth2_client.md b/docs/cookbook/src/recipes/oauth2_client.md new file mode 100644 index 0000000..5971086 --- /dev/null +++ b/docs/cookbook/src/recipes/oauth2_client.md @@ -0,0 +1,127 @@ +# OAuth2 Client Integration + +Integrating with third-party identity providers (like Google, GitHub) is a common requirement for modern applications. RustAPI provides a streamlined OAuth2 client in `rustapi-extras`. + +This recipe demonstrates how to set up an OAuth2 flow. + +## Prerequisites + +Add `rustapi-extras` with the `oauth2-client` feature to your `Cargo.toml`. + +```toml +[dependencies] +rustapi-extras = { version = "0.1.335", features = ["oauth2-client"] } +``` + +## Basic Configuration + +You can use presets for popular providers or configure a custom one. + +```rust +use rustapi_extras::oauth2::{OAuth2Config, Provider}; + +// Using a preset (Google) +let config = OAuth2Config::google( + "your-client-id", + "your-client-secret", + "https://your-app.com/auth/callback/google" +); + +// Or custom provider +let custom_config = OAuth2Config::new( + "client-id", + "client-secret", + "https://auth.example.com/authorize", + "https://auth.example.com/token", + "https://your-app.com/callback" +); +``` + +## The Authorization Flow + +1. **Redirect User**: Generate an authorization URL and redirect the user. +2. **Handle Callback**: Exchange the authorization code for an access token. + +### Step 1: Redirect User + +```rust +use rustapi_rs::prelude::*; +use rustapi_extras::oauth2::{OAuth2Client, OAuth2Config}; + +async fn login(client: State) -> impl IntoResponse { + // Generate URL with CSRF protection and PKCE + let auth_request = client.authorization_url(); + + // Store CSRF token and PKCE verifier in session (or cookie) + // In a real app, use secure, http-only cookies + // session.insert("csrf_token", auth_request.csrf_state.secret()); + // session.insert("pkce_verifier", auth_request.pkce_verifier.secret()); + + // Redirect user + Redirect::to(auth_request.url().as_str()) +} +``` + +### Step 2: Handle Callback + +```rust +use rustapi_rs::prelude::*; +use rustapi_extras::oauth2::{OAuth2Client, OAuth2Config}; + +#[derive(Deserialize)] +struct AuthCallback { + code: String, + state: String, // CSRF token +} + +async fn callback( + Query(params): Query, + client: State, + // session: Session, // Assuming session management +) -> impl IntoResponse { + // 1. Verify CSRF token from session matches params.state + + // 2. Exchange code for token + // let pkce_verifier = session.get("pkce_verifier").unwrap(); + + match client.exchange_code(¶ms.code, /* pkce_verifier */).await { + Ok(token_response) => { + // Success! You have an access token. + // Use it to fetch user info or store it. + println!("Access Token: {}", token_response.access_token()); + + // Redirect to dashboard or home + Redirect::to("/dashboard") + } + Err(e) => { + // Handle error (e.g., invalid code) + (StatusCode::BAD_REQUEST, format!("Auth failed: {}", e)).into_response() + } + } +} +``` + +## User Information + +Once you have an access token, you can fetch user details. Most providers offer a `/userinfo` endpoint. + +```rust +// Example using reqwest (feature required) +async fn get_user_info(token: &str) -> Result { + let client = reqwest::Client::new(); + client + .get("https://www.googleapis.com/oauth2/v3/userinfo") + .bearer_auth(token) + .send() + .await? + .json() + .await +} +``` + +## Best Practices + +1. **State Parameter**: Always use the `state` parameter to prevent CSRF attacks. RustAPI's `authorization_url()` generates one for you. +2. **PKCE**: Proof Key for Code Exchange (PKCE) is recommended for all OAuth2 flows, especially for public clients. RustAPI handles PKCE generation. +3. **Secure Storage**: Store tokens securely (e.g., encrypted cookies, secure session storage). Never expose access tokens in URLs or logs. +4. **HTTPS**: OAuth2 requires HTTPS callbacks in production.