better-auth-api 1.0.0-alpha.5

Plugin implementations for better-auth
use better_auth_core::{
    AuthContext, AuthError, AuthRequest, AuthResponse, AuthResult, AuthSchema, FieldValue,
    RequestMeta,
};
use serde_json::json;

use super::{UsernamePlugin, UsernameValidationOrder, request};
use crate::plugins::email_password::{
    EmailPasswordConfig, SignInUsernameFailure, sign_in_username_core,
};

impl UsernamePlugin {
    pub(crate) async fn available<S: AuthSchema>(
        &self,
        request: &AuthRequest,
        context: &AuthContext<S>,
    ) -> AuthResult<AuthResponse> {
        let username = request::availability(request)?;
        if username.is_empty() {
            return Err(AuthError::from_code(
                "UNPROCESSABLE_ENTITY",
                "INVALID_USERNAME",
                "Username is invalid",
            ));
        }
        let username = FieldValue::from(username);
        if let Some((code, message)) = self.config.validate_raw(&username).await? {
            return Err(AuthError::from_code("UNPROCESSABLE_ENTITY", code, message));
        }
        let found = context
            .database
            .get_user_by_field_value("username", &self.config.normalize(&username)?)
            .await?;
        AuthResponse::json(None, &json!({"available": found.is_none()}))
    }

    pub(super) async fn sign_in<S: AuthSchema>(
        &self,
        request: &AuthRequest,
        context: &AuthContext<S>,
    ) -> AuthResult<AuthResponse> {
        self.sign_in_with_verification(request, context, None).await
    }

    pub(crate) async fn sign_in_with_verification<S: AuthSchema>(
        &self,
        request: &AuthRequest,
        context: &AuthContext<S>,
        verification: Option<&crate::plugins::email_verification::EmailVerificationPlugin>,
    ) -> AuthResult<AuthResponse> {
        let body = request::sign_in(request)?;
        if body.username.is_empty() || body.password.is_empty() {
            return Err(AuthError::from_code(
                "UNAUTHORIZED",
                "INVALID_USERNAME_OR_PASSWORD",
                "Invalid username or password",
            ));
        }
        let username = if self.config.username_validation_order
            == Some(UsernameValidationOrder::PreNormalization)
        {
            self.config.normalize(&body.username.clone().into())?
        } else {
            body.username.clone().into()
        };
        if let Some((code, message)) = self.config.validate_raw(&username).await? {
            return Err(AuthError::from_code("UNPROCESSABLE_ENTITY", code, message));
        }
        let username = self.config.normalize(&username)?;
        let fallback = EmailPasswordConfig::default();
        let password = context
            .extensions
            .get::<EmailPasswordConfig>()
            .unwrap_or(&fallback);
        let meta =
            RequestMeta::from_request_with_config(request, &context.config.advanced.ip_address);
        let result = sign_in_username_core(
            request,
            &body,
            &username,
            password,
            verification,
            &meta,
            context,
        )
        .await;
        match result {
            Ok(result) => {
                let mut response = AuthResponse::native(None, result.response.into_field_value()?);
                if let Some(callback) = body.callback_url.filter(|value| !value.is_empty()) {
                    let _ = response.headers.insert("Location", callback);
                }
                Ok(response)
            }
            Err(SignInUsernameFailure::InvalidUsernameOrPassword) => Err(AuthError::from_code(
                "UNAUTHORIZED",
                "INVALID_USERNAME_OR_PASSWORD",
                "Invalid username or password",
            )),
            Err(SignInUsernameFailure::EmailNotVerified) => Err(AuthError::from_code(
                "FORBIDDEN",
                "EMAIL_NOT_VERIFIED",
                "Email not verified",
            )),
            Err(SignInUsernameFailure::Auth(error)) => Err(error),
        }
    }
}