better-auth-api 1.0.0-alpha.5

Plugin implementations for better-auth
use better_auth_core::{AuthResult, FieldMap, FieldValue};

use super::super::providers::{OAuthProvider, OAuthUserInfo, OAuthUserInfoResponse};

#[derive(Debug)]
pub(in crate::plugins::oauth) enum LegacySubject {
    Direct(FieldValue),
    ProfileUser(FieldValue),
}

#[derive(Debug)]
pub(in crate::plugins::oauth) struct ProviderProfile {
    pub(in crate::plugins::oauth) value: FieldValue,
    pub(in crate::plugins::oauth) legacy_subject: Option<LegacySubject>,
}

impl Default for ProviderProfile {
    fn default() -> Self {
        Self {
            value: FieldValue::Null,
            legacy_subject: None,
        }
    }
}

impl From<FieldValue> for ProviderProfile {
    fn from(value: FieldValue) -> Self {
        Self {
            value,
            legacy_subject: None,
        }
    }
}

impl ProviderProfile {
    pub(in crate::plugins::oauth) fn from_typed(
        provider: &OAuthProvider,
        response: OAuthUserInfoResponse,
    ) -> AuthResult<Self> {
        Self::from_parts(provider, response.user, response.data.into())
    }

    pub(in crate::plugins::oauth) fn from_parts(
        provider: &OAuthProvider,
        user: OAuthUserInfo,
        data: FieldValue,
    ) -> AuthResult<Self> {
        let legacy_subject = (provider.uses_legacy_profile_subject()
            && provider.account_subject.is_none())
        .then(|| LegacySubject::Direct(user.id.clone().into()));
        let mut user = user.into_native().enumerable_fields()?;
        if !provider.default_profile_includes_id() {
            let _ = user.remove("id");
        }
        Ok(Self {
            value: FieldMap::from([("user".into(), user.into()), ("data".into(), data)]).into(),
            legacy_subject,
        })
    }

    pub(in crate::plugins::oauth) fn replace_user(&mut self, user: FieldMap) -> AuthResult<()> {
        let mut response = self.value.enumerable_fields()?;
        let _ = response.insert("user".into(), user.into());
        self.value = response.into();
        Ok(())
    }

    pub(in crate::plugins::oauth) fn apply_mapped(
        &mut self,
        mapped: &FieldValue,
    ) -> AuthResult<()> {
        let mut user = self.value.model_property("user")?.enumerable_fields()?;
        user.extend(mapped.enumerable_fields()?);
        self.replace_user(user)
    }

    pub(in crate::plugins::oauth) async fn map_user(
        mut self,
        provider: &OAuthProvider,
    ) -> AuthResult<Self> {
        if let Some(mapper) = &provider.map_profile_to_user {
            let mapped = mapper
                .map_profile_native(&self.value.model_property("data")?)
                .await?;
            self.apply_mapped(&mapped)?;
        }
        Ok(self)
    }
}