rustauth-plugins 0.2.0

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

use http::Method;
use rustauth_core::api::{
    create_auth_endpoint, ApiRequest, AsyncAuthEndpoint, AuthEndpointOptions,
};
use rustauth_core::context::AuthContext;
use rustauth_core::error::RustAuthError;
use rustauth_core::plugin::AuthPlugin;

use super::change_email::{change_email, request_email_change};
use super::endpoints::{
    check_otp, send_otp, sign_in, verify_email, CHANGE_EMAIL_PATH, CHECK_PATH, CREATE_PATH,
    GET_PATH, REQUEST_CHANGE_EMAIL_PATH, RESET_PASSWORD_PATH, SEND_PATH, SIGN_IN_PATH,
    VERIFY_EMAIL_PATH,
};
use super::password::{request_password_reset, reset_password};
use super::schema::common_schema;
use super::server::{create_verification_otp, get_verification_otp};
use super::types::EmailOtpOptions;

const REQUEST_RESET_PATH: &str = "/email-otp/request-password-reset";
const FORGET_PASSWORD_PATH: &str = "/forget-password/email-otp";

pub fn paths() -> &'static [&'static str] {
    &[
        SEND_PATH,
        CREATE_PATH,
        GET_PATH,
        CHECK_PATH,
        VERIFY_EMAIL_PATH,
        SIGN_IN_PATH,
        REQUEST_RESET_PATH,
        FORGET_PASSWORD_PATH,
        RESET_PASSWORD_PATH,
        REQUEST_CHANGE_EMAIL_PATH,
        CHANGE_EMAIL_PATH,
    ]
}

pub fn register(plugin: AuthPlugin, options: EmailOtpOptions) -> AuthPlugin {
    let options = Arc::new(options);
    plugin
        .with_endpoint(endpoint(
            SEND_PATH,
            "sendEmailVerificationOTP",
            options.clone(),
            send_otp,
        ))
        .with_endpoint(endpoint(
            CREATE_PATH,
            "createEmailVerificationOTP",
            options.clone(),
            create_verification_otp,
        ))
        .with_endpoint(endpoint_with_method(
            GET_PATH,
            Method::GET,
            "getEmailVerificationOTP",
            options.clone(),
            get_verification_otp,
        ))
        .with_endpoint(endpoint(
            CHECK_PATH,
            "checkEmailVerificationOTP",
            options.clone(),
            check_otp,
        ))
        .with_endpoint(endpoint(
            VERIFY_EMAIL_PATH,
            "verifyEmailOTP",
            options.clone(),
            verify_email,
        ))
        .with_endpoint(endpoint(
            SIGN_IN_PATH,
            "signInEmailOTP",
            options.clone(),
            sign_in,
        ))
        .with_endpoint(endpoint(
            REQUEST_RESET_PATH,
            "requestPasswordResetEmailOTP",
            options.clone(),
            request_password_reset,
        ))
        .with_endpoint(endpoint(
            FORGET_PASSWORD_PATH,
            "forgetPasswordEmailOTP",
            options.clone(),
            request_password_reset,
        ))
        .with_endpoint(endpoint(
            RESET_PASSWORD_PATH,
            "resetPasswordEmailOTP",
            options.clone(),
            reset_password,
        ))
        .with_endpoint(endpoint(
            REQUEST_CHANGE_EMAIL_PATH,
            "requestEmailChangeEmailOTP",
            options.clone(),
            request_email_change,
        ))
        .with_endpoint(endpoint(
            CHANGE_EMAIL_PATH,
            "changeEmailEmailOTP",
            options,
            change_email,
        ))
}

fn endpoint<F, Fut>(
    path: &'static str,
    operation_id: &'static str,
    options: Arc<EmailOtpOptions>,
    handler: F,
) -> AsyncAuthEndpoint
where
    F: Fn(AuthContext, ApiRequest, Arc<EmailOtpOptions>) -> Fut + Send + Sync + Clone + 'static,
    Fut: std::future::Future<Output = Result<rustauth_core::api::ApiResponse, RustAuthError>>
        + Send
        + 'static,
{
    create_auth_endpoint(
        path,
        Method::POST,
        AuthEndpointOptions::new()
            .operation_id(operation_id)
            .body_schema(common_schema(path)),
        {
            let options = Arc::clone(&options);
            move |context, request| {
                let options = Arc::clone(&options);
                let handler = handler.clone();
                async move {
                    context.require_adapter()?;
                    handler(context, request, options).await
                }
            }
        },
    )
}

fn endpoint_with_method<F, Fut>(
    path: &'static str,
    method: Method,
    operation_id: &'static str,
    options: Arc<EmailOtpOptions>,
    handler: F,
) -> AsyncAuthEndpoint
where
    F: Fn(AuthContext, ApiRequest, Arc<EmailOtpOptions>) -> Fut + Send + Sync + Clone + 'static,
    Fut: std::future::Future<Output = Result<rustauth_core::api::ApiResponse, RustAuthError>>
        + Send
        + 'static,
{
    create_auth_endpoint(
        path,
        method,
        AuthEndpointOptions::new().operation_id(operation_id),
        {
            let options = Arc::clone(&options);
            move |context, request| {
                let options = Arc::clone(&options);
                let handler = handler.clone();
                async move {
                    context.require_adapter()?;
                    handler(context, request, options).await
                }
            }
        },
    )
}