openauth 0.0.4

Rust authentication toolkit.
Documentation
//! Public OpenAuth initializer.

use openauth_core::api::{
    core_auth_async_endpoints, core_endpoints, ApiRequest, ApiResponse, AsyncAuthEndpoint,
    AuthEndpoint, AuthRouter, EndpointInfo,
};
use openauth_core::context::{create_auth_context, create_auth_context_with_adapter, AuthContext};
use openauth_core::db::{DbAdapter, HookedAdapter, JoinAdapter, SchemaCreation};
use openauth_core::error::OpenAuthError;
use openauth_core::options::OpenAuthOptions;
use std::sync::Arc;

pub use openauth_core::auth::oauth;

/// Initialized OpenAuth instance.
#[derive(Clone)]
pub struct OpenAuth {
    router: AuthRouter,
    options: OpenAuthOptions,
    context: AuthContext,
    adapter: Option<Arc<dyn DbAdapter>>,
}

impl OpenAuth {
    pub fn builder() -> OpenAuthBuilder {
        OpenAuthBuilder::new()
    }

    pub fn handler(&self, request: ApiRequest) -> Result<ApiResponse, OpenAuthError> {
        self.router.handle(request)
    }

    pub async fn handler_async(&self, request: ApiRequest) -> Result<ApiResponse, OpenAuthError> {
        self.router.handle_async(request).await
    }

    pub fn options(&self) -> &OpenAuthOptions {
        &self.options
    }

    pub fn context(&self) -> &AuthContext {
        &self.context
    }

    pub fn router(&self) -> &AuthRouter {
        &self.router
    }

    pub fn endpoint_registry(&self) -> Vec<EndpointInfo> {
        self.router.endpoint_registry()
    }

    pub fn openapi_schema(&self) -> serde_json::Value {
        self.router.openapi_schema()
    }

    pub async fn create_schema(
        &self,
        file: Option<&str>,
    ) -> Result<Option<SchemaCreation>, OpenAuthError> {
        let adapter = self.adapter.as_ref().ok_or_else(|| {
            OpenAuthError::InvalidConfig(
                "OpenAuth::create_schema requires an adapter-backed instance".to_owned(),
            )
        })?;
        adapter.create_schema(&self.context.db_schema, file).await
    }

    pub async fn run_migrations(&self) -> Result<(), OpenAuthError> {
        let adapter = self.adapter.as_ref().ok_or_else(|| {
            OpenAuthError::InvalidConfig(
                "OpenAuth::run_migrations requires an adapter-backed instance".to_owned(),
            )
        })?;
        adapter.run_migrations(&self.context.db_schema).await
    }
}

#[derive(Default)]
pub struct OpenAuthBuilder {
    options: OpenAuthOptions,
    adapter: Option<Arc<dyn DbAdapter>>,
    extra_endpoints: Vec<AuthEndpoint>,
    async_endpoints: Vec<AsyncAuthEndpoint>,
}

impl OpenAuthBuilder {
    pub fn new() -> Self {
        Self::default()
    }

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

    #[must_use]
    pub fn base_url(mut self, base_url: impl Into<String>) -> Self {
        self.options = self.options.base_url(base_url);
        self
    }

    #[must_use]
    pub fn base_path(mut self, base_path: impl Into<String>) -> Self {
        self.options = self.options.base_path(base_path);
        self
    }

    #[must_use]
    pub fn secret(mut self, secret: impl Into<String>) -> Self {
        self.options = self.options.secret(secret);
        self
    }

    #[must_use]
    pub fn rate_limit(mut self, rate_limit: openauth_core::options::RateLimitOptions) -> Self {
        self.options = self.options.rate_limit(rate_limit);
        self
    }

    #[must_use]
    pub fn session(mut self, session: openauth_core::options::SessionOptions) -> Self {
        self.options = self.options.session(session);
        self
    }

    #[must_use]
    pub fn user(mut self, user: openauth_core::options::UserOptions) -> Self {
        self.options = self.options.user(user);
        self
    }

    #[must_use]
    pub fn password(mut self, password: openauth_core::options::PasswordOptions) -> Self {
        self.options = self.options.password(password);
        self
    }

    #[must_use]
    pub fn account(mut self, account: openauth_core::options::AccountOptions) -> Self {
        self.options = self.options.account(account);
        self
    }

    #[must_use]
    pub fn advanced(mut self, advanced: openauth_core::options::AdvancedOptions) -> Self {
        self.options = self.options.advanced(advanced);
        self
    }

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

    #[must_use]
    pub fn plugin(mut self, plugin: openauth_core::plugin::AuthPlugin) -> Self {
        self.options = self.options.plugin(plugin);
        self
    }

    #[must_use]
    pub fn social_provider<P>(mut self, provider: P) -> Self
    where
        P: openauth_core::oauth::oauth2::SocialOAuthProvider,
    {
        self.options = self.options.social_provider(provider);
        self
    }

    #[must_use]
    pub fn adapter<A>(mut self, adapter: A) -> Self
    where
        A: DbAdapter + 'static,
    {
        self.adapter = Some(Arc::new(adapter));
        self
    }

    #[must_use]
    pub fn adapter_arc(mut self, adapter: Arc<dyn DbAdapter>) -> Self {
        self.adapter = Some(adapter);
        self
    }

    #[must_use]
    pub fn endpoint(mut self, endpoint: AuthEndpoint) -> Self {
        self.extra_endpoints.push(endpoint);
        self
    }

    #[must_use]
    pub fn endpoints(mut self, endpoints: Vec<AuthEndpoint>) -> Self {
        self.extra_endpoints.extend(endpoints);
        self
    }

    #[must_use]
    pub fn async_endpoint(mut self, endpoint: AsyncAuthEndpoint) -> Self {
        self.async_endpoints.push(endpoint);
        self
    }

    #[must_use]
    pub fn async_endpoints(mut self, endpoints: Vec<AsyncAuthEndpoint>) -> Self {
        self.async_endpoints.extend(endpoints);
        self
    }

    pub fn build(self) -> Result<OpenAuth, OpenAuthError> {
        if let Some(adapter) = self.adapter {
            open_auth_with_adapter_and_endpoints(
                self.options,
                adapter,
                self.extra_endpoints,
                self.async_endpoints,
            )
        } else {
            open_auth_with_endpoints(self.options, self.extra_endpoints, self.async_endpoints)
        }
    }
}

/// Initialize OpenAuth with the default product endpoint set.
pub fn open_auth(options: OpenAuthOptions) -> Result<OpenAuth, OpenAuthError> {
    open_auth_with_endpoints(options, Vec::new(), Vec::new())
}

/// Initialize OpenAuth with the default product endpoint set backed by a database adapter.
pub fn open_auth_with_adapter(
    options: OpenAuthOptions,
    adapter: Arc<dyn DbAdapter>,
) -> Result<OpenAuth, OpenAuthError> {
    open_auth_with_adapter_and_endpoints(options, adapter, Vec::new(), Vec::new())
}

/// Initialize OpenAuth with product endpoints, a database adapter, and extra endpoints.
pub fn open_auth_with_adapter_and_endpoints(
    options: OpenAuthOptions,
    adapter: Arc<dyn DbAdapter>,
    extra_endpoints: Vec<AuthEndpoint>,
    async_endpoints: Vec<AsyncAuthEndpoint>,
) -> Result<OpenAuth, OpenAuthError> {
    let context = create_auth_context(options.clone())?;
    let hooked_adapter: Arc<dyn DbAdapter> = Arc::new(HookedAdapter::new(
        adapter,
        context.plugin_database_hooks.clone(),
    ));
    let adapter: Arc<dyn DbAdapter> = Arc::new(JoinAdapter::new(
        context.db_schema.clone(),
        hooked_adapter,
        options.experimental.joins,
    ));
    let context = create_auth_context_with_adapter(options.clone(), Arc::clone(&adapter))?;
    let mut endpoints = core_endpoints();
    endpoints.extend(extra_endpoints);
    let mut product_async_endpoints = core_auth_async_endpoints(Arc::clone(&adapter));
    product_async_endpoints.extend(async_endpoints);
    let router =
        AuthRouter::with_async_endpoints(context.clone(), endpoints, product_async_endpoints)?;
    Ok(OpenAuth {
        router,
        options,
        context,
        adapter: Some(adapter),
    })
}

/// Initialize OpenAuth with the default product endpoint set plus extra endpoints.
pub fn open_auth_with_endpoints(
    options: OpenAuthOptions,
    extra_endpoints: Vec<AuthEndpoint>,
    async_endpoints: Vec<AsyncAuthEndpoint>,
) -> Result<OpenAuth, OpenAuthError> {
    let context = create_auth_context(options.clone())?;
    let mut endpoints = core_endpoints();
    endpoints.extend(extra_endpoints);
    let router = AuthRouter::with_async_endpoints(context.clone(), endpoints, async_endpoints)?;
    Ok(OpenAuth {
        router,
        options,
        context,
        adapter: None,
    })
}