rustauth-plugins 0.2.0

Official RustAuth plugin modules.
Documentation
use std::sync::Arc;

use http::{Method, StatusCode};
use rustauth_core::api::{create_auth_endpoint, parse_request_body};

use super::{flow_error_response, json_response, request_cookie, rotate_session};
use crate::two_factor::cookies::{
    expire_cookie, plugin_cookie, read_signed_cookie, TRUST_DEVICE_COOKIE_NAME,
};
use crate::two_factor::flow::{current_session, validate_password};
use crate::two_factor::options::TwoFactorOptions;
use crate::two_factor::payloads::{body_options, password_schema, PasswordBody, StatusBody};
use crate::two_factor::store::{update_user_two_factor_enabled, TwoFactorStore};

pub(super) fn disable_endpoint(
    options: Arc<TwoFactorOptions>,
) -> rustauth_core::api::AsyncAuthEndpoint {
    create_auth_endpoint(
        "/two-factor/disable",
        Method::POST,
        body_options("disableTwoFactor", password_schema()),
        move |context, request| {
            let options = std::sync::Arc::clone(&options);
            async move {
                let body: PasswordBody = parse_request_body(&request)?;
                let (session, user, _cookies) = match current_session(&context, &request).await {
                    Ok(session) => session,
                    Err(error) => return flow_error_response(error),
                };
                let adapter = context.adapter_ref()?;
                if let Err(error) = validate_password(
                    &context,
                    &user.id,
                    body.password.as_deref(),
                    options.allow_passwordless,
                )
                .await
                {
                    return flow_error_response(error);
                }
                update_user_two_factor_enabled(&context, &user.id, false).await?;
                TwoFactorStore::new(adapter)
                    .delete_for_user(&user.id)
                    .await?;
                let mut cookies = rotate_session(&context, &session, &user).await?;
                let trust_cookie = plugin_cookie(
                    &context.auth_cookies.session_token,
                    TRUST_DEVICE_COOKIE_NAME,
                    options.trust_device_max_age.whole_seconds() as u64,
                );
                if let Some(value) = request_cookie(&request)
                    .and_then(|header| {
                        read_signed_cookie(&header, &trust_cookie.name, &context.secret).transpose()
                    })
                    .transpose()?
                {
                    if let Some((_, identifier)) = value.split_once('!') {
                        context
                            .verifications()?
                            .delete_verification(identifier)
                            .await?;
                    }
                    cookies.push(expire_cookie(&trust_cookie));
                }
                json_response(StatusCode::OK, &StatusBody { status: true }, cookies)
            }
        },
    )
}