larust-auth 0.9.0

A Laravel-shaped web framework for Rust.
Documentation
use larust_core::AppError;

/// Implemented by an app's `User` model (or whatever it calls its
/// authenticatable type) so `larust_support::auth`'s guard functions and
/// the [`crate::Auth`] extractor can look a user up from the id stored in
/// the session.
///
/// `find_for_auth`'s signature deliberately matches what `#[derive(Model)]`
/// already generates (`Self::find(id: i64) -> Result<Option<Self>,
/// AppError>` - see `larust-macros`' `model.rs`), so a typical impl is a
/// two-line delegation:
///
/// ```ignore
/// impl larust_support::auth::Authenticatable for User {
///     fn auth_id(&self) -> i64 { self.id }
///     async fn find_for_auth(id: i64) -> Result<Option<Self>, AppError> {
///         Self::find(id).await
///     }
/// }
/// ```
///
/// Declared as `-> impl Future<...> + Send` rather than a plain `async fn` -
/// native async-fn-in-traits doesn't propagate auto-trait bounds on the
/// returned future by default, and the [`crate::Auth`] extractor's
/// `FromRequestParts` impl (itself required to return a `Send` future by
/// axum-core's own `#[async_trait]` declaration - see GOTCHAS.md) calls
/// this through an `async` block that needs to stay `Send`. This doesn't
/// change how an implementation looks: implementing this method with a
/// plain `async fn find_for_auth(id: i64) -> Result<Option<Self>, AppError>
/// { Self::find(id).await }` still works, since `async fn`'s desugared
/// return type already satisfies `-> impl Future<...>` - only the trait's
/// own declaration needs the explicit spelling.
///
/// The two-line delegation above is the easy path for a `#[derive(Model)]`
/// (SQL-family) app, but it isn't the only path - this trait has zero
/// dependency on `#[derive(Model)]`, `larust-orm`, or SQL of any kind, so a
/// user model backed by something else entirely (a document store like
/// Firestore, an in-memory cache, a call to another service) implements it
/// exactly the same way, just with a different lookup body:
///
/// ```ignore
/// impl larust_support::auth::Authenticatable for User {
///     fn auth_id(&self) -> i64 { self.id }
///     async fn find_for_auth(id: i64) -> Result<Option<Self>, AppError> {
///         // No `#[derive(Model)]` involved - e.g. a hand-written lookup
///         // against a `larust_support::repository::Repository` impl, or
///         // any other store this type knows how to query:
///         MY_USER_REPOSITORY.find(id).await
///     }
/// }
/// ```
pub trait Authenticatable: Send + Sync + Sized + 'static {
    /// This type's guard name (Laravel's `guard('admin')`) - each distinct
    /// name gets its own session slot, so two `Authenticatable` types (say,
    /// `User` and `Admin`) can be logged in independently in the same
    /// browser session rather than overwriting each other's login state.
    ///
    /// Defaults to `"web"`, matching Laravel's own default guard name, so a
    /// single-guard app never has to think about this - and upgrading an
    /// existing single-guard app to a version of this framework with
    /// multi-guard support doesn't invalidate already-logged-in sessions
    /// (see `larust_auth::guard`'s own doc comment for why `"web"` is
    /// special-cased to the original session key).
    ///
    /// A second `Authenticatable` type that forgets to override this
    /// collides with the first on the same `"web"` session slot - there's
    /// no compile-time guard against that (a marker trait can't know how
    /// many other types implement itself), so name every guard beyond the
    /// first explicitly.
    const GUARD: &'static str = "web";

    /// The value stored in the session and used to look the user back up
    /// on a later request - typically the primary key.
    fn auth_id(&self) -> i64;

    /// Looks up a user by the id [`Authenticatable::auth_id`] returned at
    /// login time. Returns `Ok(None)` (not an error) if the id no longer
    /// resolves to a real user - e.g. the account was deleted after login.
    fn find_for_auth(
        id: i64,
    ) -> impl std::future::Future<Output = Result<Option<Self>, AppError>> + Send;
}