aiscript-runtime 0.2.0

The web runtime of AIScript programming language interpreter
Documentation
use std::collections::HashMap;

use aiscript_directive::route::SsoProvider;
use serde::Deserialize;

use super::{Config, EnvString};

#[derive(Debug, Deserialize, Default)]
pub struct SsoConfig {
    pub google: Option<OAuthProviderConfig>,
    pub github: Option<OAuthProviderConfig>,
    pub discord: Option<OAuthProviderConfig>,
    pub facebook: Option<OAuthProviderConfig>,
}

#[derive(Debug, Deserialize)]
pub struct OAuthProviderConfig {
    pub client_id: EnvString,
    pub client_secret: EnvString,
    pub redirect_url: String,
    pub scopes: Vec<String>,
}

#[inline]
fn sso_extra_fields(provider: SsoProvider) -> HashMap<&'static str, serde_json::Value> {
    match provider {
        SsoProvider::Facebook => [
            (
                "auth_endpoint",
                "https://www.facebook.com/v9.0/dialog/oauth".into(),
            ),
            (
                "token_endpoint",
                "https://graph.facebook.com/v19.0/oauth/access_token".into(),
            ),
            (
                "userinfo_endpoint",
                "https://graph.facebook.com/v19.0/me?fields=id,name,email,first_name,last_name,picture".into(),
            ),
        ]
        .into_iter()
        .collect(),
        SsoProvider::Google => [
            (
                "auth_endpoint",
                "https://accounts.google.com/o/oauth2/v2/auth".into(),
            ),
            (
                "token_endpoint",
                "https://www.googleapis.com/oauth2/v3/token".into(),
            ),
            (
                "userinfo_endpoint",
                "https://openidconnect.googleapis.com/v1/userinfo".into(),
            ),
        ]
        .into_iter()
        .collect(),
        SsoProvider::Discord => [
            ("auth_endpoint", "https://discord.com/oauth2/authorize".into()),
            ("token_endpoint", "https://discord.com/api/oauth2/token".into()),
            ("userinfo_endpoint", "https://discord.com/api/users/@me".into()),
        ]
        .into_iter()
        .collect(),
        SsoProvider::GitHub => [
            (
                "auth_endpoint",
                "https://github.com/login/oauth/authorize".into(),
            ),
            (
                "token_endpoint",
                "https://github.com/login/oauth/access_token".into(),
            ),
            ("userinfo_endpoint", "https://api.github.com/user".into()),
        ]
        .into_iter()
        .collect(),
    }
}

pub fn get_sso_fields(provider: SsoProvider) -> Option<HashMap<&'static str, serde_json::Value>> {
    let config = &Config::get().sso;
    let sso_provider = match provider {
        SsoProvider::Facebook => config.facebook.as_ref(),
        SsoProvider::Google => config.google.as_ref(),
        SsoProvider::Discord => config.discord.as_ref(),
        SsoProvider::GitHub => config.github.as_ref(),
    }?;

    let mut fields: HashMap<&'static str, serde_json::Value> = [
        (
            "client_id",
            serde_json::Value::String(sso_provider.client_id.to_string()),
        ),
        (
            "client_secret",
            serde_json::Value::String(sso_provider.client_secret.to_string()),
        ),
        (
            "redirect_url",
            serde_json::Value::String(sso_provider.redirect_url.to_string()),
        ),
        (
            "scopes",
            serde_json::Value::Array(
                sso_provider
                    .scopes
                    .iter()
                    .map(|v| serde_json::Value::String(v.to_string()))
                    .collect(),
            ),
        ),
    ]
    .into_iter()
    .collect();

    fields.extend(sso_extra_fields(provider));

    Some(fields)
}