better-auth-api 1.0.0-alpha.5

Plugin implementations for better-auth
use crate::plugins::json_body;
use better_auth_core::{AuthError, AuthRequest, AuthResult, endpoint_input::ValidatedBody};
use serde::{Deserialize, Serialize};
use serde_json::Value;

#[derive(Clone, Deserialize, Serialize)]
pub(super) struct VerifyBody {
    pub message: String,
    pub signature: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub email: Option<String>,
}
pub(super) fn nonce(req: &AuthRequest) -> AuthResult<ValidatedBody> {
    let body = req.input_body()?;
    if let Some(value) = &body {
        if !value.is_object() {
            return Err(json_body::validation_error(&json_body::invalid_type(
                "body",
                "object",
                Some(value),
            )));
        }
        if let Some(error) = super::unknown_keys(value, &[]) {
            return Err(json_body::validation_error(&error));
        }
    }
    Ok(ValidatedBody::new(body, ()))
}
pub(super) fn verify(req: &AuthRequest, anonymous: bool) -> AuthResult<ValidatedBody> {
    let input = req.input_body()?;
    let body = input
        .as_ref()
        .filter(|body| body.is_object())
        .ok_or_else(|| {
            json_body::validation_error(&json_body::invalid_type("body", "object", input.as_ref()))
        })?;
    let mut errors = Vec::new();
    let mut valid_types = true;
    for field in ["message", "signature"] {
        match body.get(field) {
            Some(Value::String(value)) if value.is_empty() => errors.push(format!(
                "[body.{field}] Too small: expected string to have >=1 characters"
            )),
            Some(Value::String(_)) => {}
            value => {
                valid_types = false;
                errors.push(json_body::invalid_type(
                    &format!("body.{field}"),
                    "string",
                    value,
                ));
            }
        }
    }
    let email = body.get("email").and_then(Value::as_str);
    match body.get("email") {
        Some(Value::String(value)) if !json_body::valid_email(value)? => {
            errors.push("[body.email] Invalid email address".to_owned())
        }
        None | Some(Value::String(_)) => {}
        value => {
            valid_types = false;
            errors.push(json_body::invalid_type("body.email", "string", value));
        }
    }
    if let Some(error) = super::unknown_keys(body, &["message", "signature", "email"]) {
        errors.push(error);
    }
    if valid_types && !anonymous && email.is_none_or(str::is_empty) {
        errors.push(
            "[body.email] Email is required when the anonymous plugin option is disabled."
                .to_owned(),
        );
    }
    if !errors.is_empty() {
        return Err(json_body::validation_error(&errors.join("; ")));
    }
    if req.original_request().is_none()
        && better_auth_core::hooks::current_request_hook_context()
            .is_some_and(|context| !context.is_http)
    {
        return Err(json_body::validation_error("Request is required"));
    }
    let typed: VerifyBody = serde_json::from_value(body.clone())?;
    Ok(ValidatedBody::new(
        Some(serde_json::to_value(&typed)?),
        typed,
    ))
}
pub(super) fn read(req: &AuthRequest, anonymous: bool) -> AuthResult<VerifyBody> {
    if let Some(body) = req.validated_body::<VerifyBody>() {
        return Ok(body.clone());
    }
    verify(req, anonymous)?
        .get::<VerifyBody>()
        .cloned()
        .ok_or_else(|| AuthError::internal("SIWE validator returned a different body type"))
}