tinkr 0.0.43

Tinkr is a web framework for quickly building full-stack web applications with Leptos.
Documentation
#[cfg(feature = "ssr")]
use oauth2::basic::BasicClient;
#[cfg(feature = "ssr")]
use oauth2::{AuthUrl, ClientId, ClientSecret, RedirectUrl, TokenUrl};

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum OAuthProvider {
    Github,
    Google,
    Discord,
}

impl OAuthProvider {
    pub fn as_str(&self) -> &str {
        match self {
            OAuthProvider::Github => "github",
            OAuthProvider::Google => "google",
            OAuthProvider::Discord => "discord",
        }
    }
}

#[cfg(feature = "ssr")]
pub struct OAuthConfig {
    pub provider: OAuthProvider,
    pub client_id: String,
    pub client_secret: String,
    pub auth_url: String,
    pub token_url: String,
    pub user_info_url: String,
}

#[cfg(feature = "ssr")]
impl OAuthConfig {
    pub fn github() -> Result<Self, String> {
        Ok(Self {
            provider: OAuthProvider::Github,
            client_id: std::env::var("GITHUB_CLIENT_ID")
                .map_err(|_| "Missing GITHUB_CLIENT_ID environment variable")?,
            client_secret: std::env::var("GITHUB_CLIENT_SECRET")
                .map_err(|_| "Missing GITHUB_CLIENT_SECRET environment variable")?,
            auth_url: "https://github.com/login/oauth/authorize".to_string(),
            token_url: "https://github.com/login/oauth/access_token".to_string(),
            user_info_url: "https://api.github.com/user".to_string(),
        })
    }

    pub fn google() -> Result<Self, String> {
        Ok(Self {
            provider: OAuthProvider::Google,
            client_id: std::env::var("GOOGLE_CLIENT_ID")
                .map_err(|_| "Missing GOOGLE_CLIENT_ID environment variable")?,
            client_secret: std::env::var("GOOGLE_CLIENT_SECRET")
                .map_err(|_| "Missing GOOGLE_CLIENT_SECRET environment variable")?,
            auth_url: "https://accounts.google.com/o/oauth2/v2/auth".to_string(),
            token_url: "https://www.googleapis.com/oauth2/v3/token".to_string(),
            user_info_url: "https://www.googleapis.com/oauth2/v2/userinfo".to_string(),
        })
    }

    pub fn discord() -> Result<Self, String> {
        Ok(Self {
            provider: OAuthProvider::Discord,
            client_id: std::env::var("DISCORD_CLIENT_ID")
                .map_err(|_| "Missing DISCORD_CLIENT_ID environment variable")?,
            client_secret: std::env::var("DISCORD_CLIENT_SECRET")
                .map_err(|_| "Missing DISCORD_CLIENT_SECRET environment variable")?,
            auth_url: "https://discord.com/api/oauth2/authorize".to_string(),
            token_url: "https://discord.com/api/oauth2/token".to_string(),
            user_info_url: "https://discord.com/api/users/@me".to_string(),
        })
    }

    pub fn get_redirect_url() -> String {
        std::env::var("TINKR_AUTH_URL").unwrap_or_else(|_| "http://localhost:3000".to_string())
    }

    pub fn build_client(&self) -> Result<BasicClient, String> {
        let auth_url =
            AuthUrl::new(self.auth_url.clone()).map_err(|e| format!("Invalid auth URL: {}", e))?;
        let token_url = TokenUrl::new(self.token_url.clone())
            .map_err(|e| format!("Invalid token URL: {}", e))?;

        let redirect_url = format!(
            "{}/api/auth/callback/{}",
            Self::get_redirect_url(),
            self.provider.as_str()
        );

        let redirect =
            RedirectUrl::new(redirect_url).map_err(|e| format!("Invalid redirect URL: {}", e))?;

        Ok(BasicClient::new(
            ClientId::new(self.client_id.clone()),
            Some(ClientSecret::new(self.client_secret.clone())),
            auth_url,
            Some(token_url),
        )
        .set_redirect_uri(redirect))
    }
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct OAuthUserInfo {
    pub id: String,
    pub email: Option<String>,
    pub name: Option<String>,
    pub avatar: Option<String>,
}