maxcountryman / tower-sessions

🥠 Sessions as a `tower` and `axum` middleware.
MIT License
249 stars 43 forks source link
axum axum-middleware cookies sessions tower

tower-sessions

🥠 Sessions as a `tower` and `axum` middleware.

🎨 Overview

This crate provides sessions, key-value pairs associated with a site visitor, as a tower middleware.

It offers:

This crate's session implementation is inspired by the Django sessions middleware and it provides a transliteration of those semantics.

Session stores

Session data persistence is managed by user-provided types that implement SessionStore. What this means is that applications can and should implement session stores to fit their specific needs.

That said, a number of session store implmentations already exist and may be useful starting points.

Crate Persistent Description
tower-sessions-dynamodb-store Yes DynamoDB session store
tower-sessions-file-store Yes Local filesystem store
tower-sessions-firestore-store Yes Firestore session store
tower-sessions-libsql-store Yes libSQL session store
tower-sessions-mongodb-store Yes MongoDB session store
tower-sessions-moka-store No Moka session store
tower-sessions-redis-store Yes Redis via fred session store
tower-sessions-rorm-store Yes SQLite, Postgres and Mysql session store provided by rorm
tower-sessions-rusqlite-store Yes Rusqlite session store
tower-sessions-sled-store Yes Sled session store
tower-sessions-sqlx-store Yes SQLite, Postgres, and MySQL session stores
tower-sessions-surrealdb-store Yes SurrealDB session store

Have a store to add? Please open a PR adding it.

User session management

To facilitate authentication and authorization, we've built axum-login on top of this crate. Please check it out if you're looking for a generalized auth solution.

📦 Install

To use the crate in your project, add the following to your Cargo.toml file:

[dependencies]
tower-sessions = "0.13.0"

🤸 Usage

axum Example

use std::net::SocketAddr;

use axum::{response::IntoResponse, routing::get, Router};
use serde::{Deserialize, Serialize};
use time::Duration;
use tower_sessions::{Expiry, MemoryStore, Session, SessionManagerLayer};

const COUNTER_KEY: &str = "counter";

#[derive(Default, Deserialize, Serialize)]
struct Counter(usize);

async fn handler(session: Session) -> impl IntoResponse {
    let counter: Counter = session.get(COUNTER_KEY).await.unwrap().unwrap_or_default();
    session.insert(COUNTER_KEY, counter.0 + 1).await.unwrap();
    format!("Current count: {}", counter.0)
}

#[tokio::main]
async fn main() {
    let session_store = MemoryStore::default();
    let session_layer = SessionManagerLayer::new(session_store)
        .with_secure(false)
        .with_expiry(Expiry::OnInactivity(Duration::seconds(10)));

    let app = Router::new().route("/", get(handler)).layer(session_layer);

    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
    axum::serve(listener, app.into_make_service())
        .await
        .unwrap();
}

You can find this example as well as other example projects in the example directory.

[!NOTE] See the crate documentation for more usage information.

🦺 Safety

This crate uses #![forbid(unsafe_code)] to ensure everything is implemented in 100% safe Rust.

🛟 Getting Help

We've put together a number of examples to help get you started. You're also welcome to open a discussion and ask additional questions you might have.

👯 Contributing

We appreciate all kinds of contributions, thank you!