//! Google/Gmail OAuth2 provider. //! //! Implements OAuth2 for Gmail access via IMAP with XOAUTH2. //! Uses default trait implementations for token exchange. //! //! See: https://developers.google.com/identity/protocols/oauth2 use crate::oauth::provider::{OAuthProvider, OAuthProviderConfig}; /// Google/Gmail OAuth2 provider. /// /// Uses FormBody authentication (client_id + client_secret in request body). pub struct GoogleProvider { client_id: String, client_secret: String, config: OAuthProviderConfig, } impl GoogleProvider { /// Creates a new Google OAuth provider. /// /// Google requires a client secret even for desktop apps. pub fn new(client_id: impl Into, client_secret: impl Into) -> Self { Self { client_id: client_id.into(), client_secret: client_secret.into(), config: OAuthProviderConfig { auth_url: "https://accounts.google.com/o/oauth2/v2/auth".to_string(), token_url: "https://oauth2.googleapis.com/token".to_string(), scopes: vec![ "https://mail.google.com/".to_string(), // Full Gmail access (IMAP/SMTP) "openid".to_string(), "email".to_string(), "profile".to_string(), ], uses_jmap: false, jmap_session_url: None, imap_server: Some("imap.gmail.com".to_string()), imap_port: Some(993), smtp_server: Some("smtp.gmail.com".to_string()), smtp_port: Some(587), userinfo_url: Some("https://www.googleapis.com/oauth2/v2/userinfo".to_string()), email_json_path: vec!["email"], }, } } } impl OAuthProvider for GoogleProvider { fn id(&self) -> &'static str { "google" } fn display_name(&self) -> &'static str { "Google / Gmail" } fn config(&self) -> &OAuthProviderConfig { &self.config } fn client_id(&self) -> &str { &self.client_id } fn client_secret(&self) -> Option<&str> { Some(&self.client_secret) } fn customize_auth_url(&self, url: &mut String) { // Google-specific: request refresh token and always show consent url.push_str("&access_type=offline"); url.push_str("&prompt=consent"); } // Uses default exchange_code, refresh_token, get_user_email implementations } /// Generates an XOAUTH2 authentication string for IMAP/SMTP. /// /// Format: base64("user=" + email + "\x01auth=Bearer " + access_token + "\x01\x01") pub fn generate_xoauth2_string(email: &str, access_token: &str) -> String { use base64::{engine::general_purpose::STANDARD, Engine}; let auth_string = format!("user={}\x01auth=Bearer {}\x01\x01", email, access_token); STANDARD.encode(auth_string.as_bytes()) }