http-rs / tide

Fast and friendly HTTP server framework for async Rust
https://docs.rs/tide
Apache License 2.0
5.03k stars 322 forks source link

SessionMiddleware and RedisSessionStore #859

Open alucryd opened 2 years ago

alucryd commented 2 years ago

Tide does not recommend the use of MemoryStore in production, but using RedisSessionStore from async-redis-session doesn't seem to be straightforward. Do you guys have a working example?

mdtusz commented 2 years ago

I don't have a shareable working example at the moment without some cleanup work, but you can use redis sessions with something like this:

use async_redis_session::RedisSessionStore;
use tide::sessions::SessionMiddleware;

#[async_std::main]
async fn main() -> tide::Result<()> {
    let server = tide::new();

    let redis_client = redis::Client::open("rediss://some_address@user:pass:port/").unwrap();

    let store = RedisSessionStore::from_client(redis_client).with_prefix("user-session:");
    let sessions_mw = SessionMiddleware::new(store, "c0oK1eSeCr3t".as_bytes())
       .with_cookie_name("sid")
       .with_cookie_path("/")
       .without_save_unchanged();

    server.with(sessions_mw);

    // Rest of your app setup.

    Ok(())
}

Then in request handlers, you can access the session as described in the docs.

maxcountryman commented 2 years ago

You will likely need to pin async-redis-session = "0.1.1" since Tide still has not been updated to support versions beyond that.

Once you do, all you need to do from there is something like:

let mut app = tide::new();
let session_store = RedisSessionStore::new("redis://localhost:6379").expect("Could not connect to Redis");
app.with(
    SessionMiddleware::new(session_store, b"change me to some super secret bytes"),
 );
nyxtom commented 2 years ago

I've implemented something similar in my full stack app sample using the async-redis-session = "0.2.2" https://github.com/nyxtom/rust-twitter-clone/blob/master/src/main.rs#L41-L44 The versions just need to line up in order for it to work. At the moment I'm using 0.17.0-beta.1.

[package]
name = "web"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
async-std = { version = "1.12.0", features = ["attributes"] }
handlebars = "4.3.1"
serde = { version = "1.0.138", features = ["derive"] }
tide = { version = "0.17.0-beta.1", features = ["sessions", "h1-server", "cookies"] }
tide-flash = { version = "0.1.1-beta.1" }
env_logger = "0.9.0"
dotenv = "0.15.0"
time = { version = "0.2.6", default-features = false, features = ["std"] }
serde_json = "1.0.82"
async-redis-session = "0.2.2"
libreauth = { version = "0.15.0", features = ["oath-uri"] }
qrcode = "0.12.0"
async-trait = "0.1.56"
validator = { version = "0.15.0", features = ["derive"] }
use async_redis_session::RedisSessionStore;
use registry::State;
use serde::{Deserialize, Serialize};
use tide::log::LogMiddleware;
use tide_flash::{cookies::CookieStore, FlashMiddleware};

mod registry;
mod request_ext;
mod route_ext;
mod routes;
mod templates;

mod prelude {
    pub use crate::request_ext::*;
    pub use crate::route_ext::*;
    pub use tide_flash::ext::*;
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Claims {
    sub: String,
    username: String,
    uid: u64,
    exp: usize,
    totp_enabled: bool,
    totp_attempt: usize,
    totp: Option<usize>,
}

#[async_std::main]
async fn main() -> tide::Result<()> {
    let mut app = tide::with_state(State::new());
    dotenv::dotenv().ok();
    env_logger::init();

    app.with(LogMiddleware::new());

    // configure openid connect and session middleware
    let session_secret = std::env::var("SESSION_SECRET")?;
    let redis_url = std::env::var("REDIS_URL")?;
    app.with(tide::sessions::SessionMiddleware::new(
        RedisSessionStore::new(redis_url)?,
        session_secret.as_bytes(),
    ));
    app.with(FlashMiddleware::new(CookieStore::default()));
    routes::configure(&mut app);

    let host = std::env::var("HOST").unwrap_or(String::from("0.0.0.0"));
    let port: u16 = std::env::var("PORT")?.parse()?;
    app.listen((host, port)).await?;

    Ok(())
}