1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
//! OAuth2 authentication module
//!
//! This module provides OAuth2 authentication support for acton-htmx applications,
//! including:
//! - Google OAuth2 (with OpenID Connect)
//! - GitHub OAuth2
//! - Generic OpenID Connect provider
//!
//! # Features
//!
//! - **CSRF Protection**: State tokens prevent cross-site request forgery attacks
//! - **PKCE Support**: Proof Key for Code Exchange for enhanced security
//! - **Account Linking**: Link multiple OAuth providers to a single user account
//! - **Type Safety**: Strongly typed providers and configurations
//! - **acton-reactive Integration**: State management via agents
//!
//! # Example Usage
//!
//! ```rust,no_run
//! use acton_htmx::oauth2::{OAuthConfig, ProviderConfig};
//! use axum::{Router, routing::get};
//!
//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
//! // Configure OAuth2 providers
//! let oauth_config = OAuthConfig {
//! google: Some(ProviderConfig {
//! client_id: std::env::var("GOOGLE_CLIENT_ID")?,
//! client_secret: std::env::var("GOOGLE_CLIENT_SECRET")?,
//! redirect_uri: "http://localhost:3000/auth/google/callback".to_string(),
//! scopes: vec!["openid".to_string(), "email".to_string(), "profile".to_string()],
//! auth_url: None,
//! token_url: None,
//! userinfo_url: None,
//! }),
//! github: Some(ProviderConfig {
//! client_id: std::env::var("GITHUB_CLIENT_ID")?,
//! client_secret: std::env::var("GITHUB_CLIENT_SECRET")?,
//! redirect_uri: "http://localhost:3000/auth/github/callback".to_string(),
//! scopes: vec!["read:user".to_string(), "user:email".to_string()],
//! auth_url: None,
//! token_url: None,
//! userinfo_url: None,
//! }),
//! oidc: None,
//! };
//!
//! // Add OAuth2 routes to your router
//! let app = Router::new()
//! .route("/auth/:provider", get(acton_htmx::oauth2::handlers::initiate_oauth))
//! .route("/auth/:provider/callback", get(acton_htmx::oauth2::handlers::handle_oauth_callback))
//! .route("/auth/:provider/unlink", get(acton_htmx::oauth2::handlers::unlink_oauth_account));
//! # Ok(())
//! # }
//! ```
//!
//! # Configuration
//!
//! OAuth2 providers are configured via the `OAuthConfig` struct, which should be
//! stored in your application configuration:
//!
//! ```toml
//! [oauth2.google]
//! client_id = "your-google-client-id"
//! client_secret = "your-google-client-secret"
//! redirect_uri = "http://localhost:3000/auth/google/callback"
//! scopes = ["openid", "email", "profile"]
//!
//! [oauth2.github]
//! client_id = "your-github-client-id"
//! client_secret = "your-github-client-secret"
//! redirect_uri = "http://localhost:3000/auth/github/callback"
//! scopes = ["read:user", "user:email"]
//! ```
//!
//! # Security Considerations
//!
//! - **State Tokens**: CSRF state tokens are generated using cryptographically secure random
//! number generators and expire after 10 minutes
//! - **PKCE**: All providers use PKCE (Proof Key for Code Exchange) to prevent authorization
//! code interception attacks
//! - **State Validation**: State tokens are validated server-side using the OAuth2Agent
//! - **One-Time Use**: State tokens are removed after successful validation
//! - **Session Storage**: PKCE verifiers are stored in secure HTTP-only session cookies
//!
//! # Database Schema
//!
//! The OAuth2 module requires the `oauth_accounts` table (see migration 002):
//!
//! ```sql
//! CREATE TABLE oauth_accounts (
//! id BIGSERIAL PRIMARY KEY,
//! user_id BIGINT NOT NULL,
//! provider TEXT NOT NULL CHECK (provider IN ('google', 'github', 'oidc')),
//! provider_user_id TEXT NOT NULL,
//! email TEXT NOT NULL,
//! name TEXT,
//! avatar_url TEXT,
//! created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
//! updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
//! CONSTRAINT fk_oauth_accounts_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
//! CONSTRAINT unique_oauth_account UNIQUE (provider, provider_user_id)
//! );
//! ```
pub use ;
pub use ;
pub use OAuthAccount;
pub use ;
pub use ;