rustauth-plugins 0.3.0

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

use rustauth_core::context::AuthContext;
use rustauth_core::db::{Session, User};
use rustauth_core::error::RustAuthError;
use time::Duration;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OneTimeTokenSession {
    pub session: Session,
    pub user: User,
}

pub type GenerateToken =
    Arc<dyn Fn(&OneTimeTokenSession, &AuthContext) -> Result<String, RustAuthError> + Send + Sync>;
pub type HashToken = Arc<dyn Fn(&str) -> Result<String, RustAuthError> + Send + Sync>;

#[derive(Clone)]
pub enum StoreToken {
    Plain,
    Hashed,
    Custom(HashToken),
}

impl StoreToken {
    pub fn custom<F>(hash: F) -> Self
    where
        F: Fn(&str) -> Result<String, RustAuthError> + Send + Sync + 'static,
    {
        Self::Custom(Arc::new(hash))
    }
}

#[derive(Clone)]
pub struct OneTimeTokenOptions {
    pub expires_in: Duration,
    pub disable_client_request: bool,
    pub generate_token: Option<GenerateToken>,
    pub disable_set_session_cookie: bool,
    pub store_token: StoreToken,
    pub set_ott_header_on_new_session: bool,
}

impl Default for OneTimeTokenOptions {
    fn default() -> Self {
        Self {
            expires_in: Duration::minutes(3),
            disable_client_request: false,
            generate_token: None,
            disable_set_session_cookie: false,
            store_token: StoreToken::Plain,
            set_ott_header_on_new_session: false,
        }
    }
}

impl OneTimeTokenOptions {
    #[must_use]
    pub fn expires_in(mut self, expires_in: Duration) -> Self {
        self.expires_in = expires_in;
        self
    }

    #[must_use]
    pub fn disable_client_request(mut self, disable: bool) -> Self {
        self.disable_client_request = disable;
        self
    }

    #[must_use]
    pub fn generate_token<F>(mut self, generate: F) -> Self
    where
        F: Fn(&OneTimeTokenSession, &AuthContext) -> Result<String, RustAuthError>
            + Send
            + Sync
            + 'static,
    {
        self.generate_token = Some(Arc::new(generate));
        self
    }

    #[must_use]
    pub fn disable_set_session_cookie(mut self, disable: bool) -> Self {
        self.disable_set_session_cookie = disable;
        self
    }

    #[must_use]
    pub fn store_token(mut self, store_token: StoreToken) -> Self {
        self.store_token = store_token;
        self
    }

    #[must_use]
    pub fn set_ott_header_on_new_session(mut self, set_header: bool) -> Self {
        self.set_ott_header_on_new_session = set_header;
        self
    }

    #[must_use]
    pub fn builder() -> OneTimeTokenOptionsBuilder {
        OneTimeTokenOptionsBuilder::default()
    }

    pub(crate) fn to_value(&self) -> serde_json::Value {
        serde_json::json!({
            "expiresIn": self.expires_in.whole_minutes(),
            "disableClientRequest": self.disable_client_request,
            "disableSetSessionCookie": self.disable_set_session_cookie,
            "storeToken": self.store_token.as_metadata_value(),
            "setOttHeaderOnNewSession": self.set_ott_header_on_new_session,
        })
    }
}

#[derive(Clone, Default)]
pub struct OneTimeTokenOptionsBuilder {
    expires_in: Option<Duration>,
    disable_client_request: Option<bool>,
    generate_token: Option<GenerateToken>,
    disable_set_session_cookie: Option<bool>,
    store_token: Option<StoreToken>,
    set_ott_header_on_new_session: Option<bool>,
}

impl OneTimeTokenOptionsBuilder {
    pub fn build(self) -> OneTimeTokenOptions {
        let defaults = OneTimeTokenOptions::default();
        OneTimeTokenOptions {
            expires_in: self.expires_in.unwrap_or(defaults.expires_in),
            disable_client_request: self
                .disable_client_request
                .unwrap_or(defaults.disable_client_request),
            generate_token: self.generate_token.or(defaults.generate_token),
            disable_set_session_cookie: self
                .disable_set_session_cookie
                .unwrap_or(defaults.disable_set_session_cookie),
            store_token: self.store_token.unwrap_or(defaults.store_token),
            set_ott_header_on_new_session: self
                .set_ott_header_on_new_session
                .unwrap_or(defaults.set_ott_header_on_new_session),
        }
    }
}

impl StoreToken {
    fn as_metadata_value(&self) -> &'static str {
        match self {
            Self::Plain => "plain",
            Self::Hashed => "hashed",
            Self::Custom(_) => "custom-hasher",
        }
    }
}