Crate authfix

Source
Expand description

Authfix provides a quick and easy way to add authentication to your Actix Web app.

The AuthToken extractor enables straightforward access to the authenticated user in secured handlers.

§Quick start

For a quick start, use the working examples from authfix-examples

§Session Authentication

Currently, only session authentication is supported (OIDC support is planned). This implementation is built on actix-session. Authfix re-exports actix-session for this reason.

The session authentication flow can be configured in two modes.

  1. API based (default)
    • It is designed to work with Single Page Applications, so it offers a JSON API for login, logout and mfa verification. Redirects are then handled by the SPA.
  2. Redirect based
    • Instead of returning 401 for unauthorized requests, it redirects the user to the login page. The login flow is completely handled by the browser. You just have to define the login, mfa and logout pages. The redirects are going to the same routes as defined in Routes. To activate this mode, set with_redirect_flow() in SessionLoginAppBuilder.

§Async traits

To use this library, it is necessary to implement certrain traits (e.g.: LoadUserByCredentials). Wherever possible, native async syntax is supported.

However, some of the traits must be dyn compatible, so the async_trait crate is used for those (e.g. for MfaHandleMfaRequest).

Authfix re-exports the authfix::async_trait macro.

§Examples

§Session based authentication

use actix_web::{HttpResponse, HttpServer, Responder, cookie::Key, get};
use authfix::{
    AuthToken,
    login::{LoadUserByCredentials, LoadUserError, LoginToken},
    session::{AccountInfo, app_builder::SessionLoginAppBuilder},
};
use serde::{Deserialize, Serialize};

// A user intended for session authentication must derive Serialize, and Deserialize.
#[derive(Serialize, Deserialize)]
struct User {
    name: String,
}

// AccountInfo trait is used for disabling the user or to lock the account
// The user is enabled by default
impl AccountInfo for User {}

// Struct that handles the authentication
struct AuthenticationService;

// LoadUsersByCredentials uses async_trait, so its needed when implementing the trait for AuthenticationService
// async_trait is re-exported by authfix.
impl LoadUserByCredentials for AuthenticationService {
    type User = User;

    async fn load_user(&self, login_token: &LoginToken) -> Result<Self::User, LoadUserError> {
        // load user by email logic and check password
        // currently authfix does not provide hashing functions, you can use for example https://docs.rs/argon2/latest/argon2/
        if login_token.email == "test@example.org" && login_token.password == "password" {
            Ok(User {
                name: "Johnny".to_owned(),
            })
        } else {
            Err(LoadUserError::LoginFailed)
        }
    }
}

// You have access to the user via the AuthToken extractor in secured routes.
#[get("/secured")]
async fn secured(auth_token: AuthToken<User>) -> impl Responder {
    let user = auth_token.authenticated_user();
    HttpResponse::Ok().json(&*user)
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let key = Key::generate();
    HttpServer::new(move || {
        // SessionLoginAppBuilder is the simplest way to create an App instance configured with session based authentication
        // This default config registers handlers for: /login, /logout and /login/mfa.
        SessionLoginAppBuilder::create(AuthenticationService, key.clone())
            .build()
            .service(secured)
    })
    .bind("127.0.0.1:7080")?
    .run()
    .await
}

§Configure the session

use actix_web::{HttpResponse, HttpServer, Responder, cookie::Key, get, middleware::Logger};
use authfix::{
    AuthToken,
    login::{LoadUserByCredentials, LoadUserError, LoginToken},
    session::{
        AccountInfo,
        actix_session::{
            SessionMiddleware,
            config::{PersistentSession, SessionLifecycle},
            storage::CookieSessionStore,
        },
        app_builder::SessionLoginAppBuilder,
    },
};
use serde::{Deserialize, Serialize};

// A user intended for session authentication must derive or implement Serialize, and Deserialize.
#[derive(Serialize, Deserialize)]
struct User {
    name: String,
}

impl AccountInfo for User {}

// Struct that handles the authentication
struct AuthenticationService;

// LoadUsersByCredentials uses async_trait, so its needed when implementing the trait for AuthenticationService
// async_trait is re-exported by authfix.
impl LoadUserByCredentials for AuthenticationService {
    type User = User;

    async fn load_user(&self, login_token: &LoginToken) -> Result<Self::User, LoadUserError> {
        // load user by email logic and check password
        // currently authfix does not provide hashing functions, you can use for example https://docs.rs/argon2/latest/argon2/
        if login_token.email == "test@example.org" && login_token.password == "password" {
            Ok(User {
                name: "Johnny".to_owned(),
            })
        } else {
            Err(LoadUserError::LoginFailed)
        }
    }
}

// You have access to the user via the AuthToken extractor in secured routes.
#[get("/secured")]
async fn secured(auth_token: AuthToken<User>) -> impl Responder {
    let user = auth_token.authenticated_user();
    HttpResponse::Ok().json(&*user)
}

pub fn session_config(key: Key) -> SessionMiddleware<CookieSessionStore> {
    let persistent_session = PersistentSession::default();
    let lc = SessionLifecycle::PersistentSession(persistent_session);
    SessionMiddleware::builder(CookieSessionStore::default(), key)
        .cookie_name("sessionId".to_string())
        .cookie_http_only(true)
        .cookie_same_site(actix_web::cookie::SameSite::Strict)
        .cookie_secure(false)
        .session_lifecycle(lc)
        .build()
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let key = Key::generate();
    HttpServer::new(move || {
        // SessionLoginAppBuilder is the simplest way to create an App instance configured with session based authentication
        SessionLoginAppBuilder::create_with_session_middleware(
            AuthenticationService,
            session_config(key.clone()),
        )
        // create App instance with build()
        .build()
        .wrap(Logger::default())
        .service(secured)
    })
    .bind("127.0.0.1:7080")?
    .run()
    .await
}

Modules§

errors
Error types for all kinds of authentication
login
middleware
multifactor
session

Structs§

AuthToken
Extractor that holds the authenticated user.

Traits§

AuthTokenExt
Extension to get the AuthToken from HttpRequest
AuthenticationProvider
Main component used by the middleware to handle the actual authentication mechanism

Attribute Macros§

async_trait
Re-exported async_trait macro for use in trait definitions.