Best way to create a session cookie if it's not present and persist to a database using axum and tower_cookies? – Cookies

by
Ali Hasan
cookies middleware rust-analyzer rust-axum

Quick Fix: Modify the code to use deadpool-sqlite instead of rusqlite for handling the asynchronous communication with the SQLite database.This will resolve the errors faced when attempting to set and persist session cookies within the set_and_store_cookie_if_absent function.

The Problem:

Write a middleware in Axum which checks if a session cookie is present in the request, and if not, creates one and stores it in the database. The middleware should use the tower_cookies crate to simplify cookie handling.

The Solutions:

Solution 1: Using Cookies Manager Layer

To use the tower_cookies crate more efficiently and avoid dealing with raw headers, you can employ a middleware function that utilizes the `Cookies` struct. Here’s how you can achieve that:

use axum::{
    http::{header, StatusCode, Response},
    response::IntoResponse,
    Extension,
    middleware::Next,
};
use tower_cookies::{Cookie, Cookies};
use deadpool_sqlite::{Manager, Pool};
use rusqlite::Connection;

pub async fn set_and_store_cookie_if_absent<B>(
    Extension(pool): Extension<Manager<Pool<Connection>>>,
    mut cookies: Cookies,
    request: Request<B>,
    next: Next<B>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    if cookies.get(SESSION_COOKIE).is_none() {
        // Check if the session cookie is absent.

        // Generate a new session ID.
        session_id = generate_session_id();

        // Create a new session entry in the database.
        let mut conn = pool.get().await.map_err(|e| {
            (
                StatusCode::INTERNAL_SERVER_ERROR,
                format!("Failed to get connection from pool: {}", e),
            )
        })?;
        conn.execute(
            "INSERT INTO sessions (session_id) VALUES (?)",
            &[&session_id],
        )
        .map_err(|e| {
            (
                StatusCode::INTERNAL_SERVER_ERROR,
                format!("Failed to insert session into database: {}", e),
            )
        })?;

        // Add the session cookie to the response.
        cookies.add_private(
            Cookie::new(SESSION_COOKIE, session_id.clone()),
        );
    }

    // Continue processing the request.
    let response = next.run(request).await;

    // Return the response.
    Ok(response)
}

In this middleware function:

  1. We use Extension::<Manager<Pool<Connection>>> to access the database pool.
  2. We check if the session cookie is absent using cookies.get(SESSION_COOKIE).is_none().
  3. If the session cookie is absent, we generate a new session ID and insert a new session entry into the database.
  4. We add the session cookie to the response using cookies.add_private(Cookie::new(SESSION_COOKIE, session_id.clone())).
  5. We continue processing the request using next.run(request).await.

By utilizing the Cookies struct and the middleware function, you can create and persist session cookies to a database efficiently and seamlessly.

Q&A

How to create a session cookie in axum using tower_cookies middleware?

Use CookieManagerLayer middleware to get access to Cookies struct.

Where to store session cookie in axum?

Use a State to store session cookie.

How to check if session cookie exists?

Use cookies.get to check if session cookie exists.

Video Explanation:

The following video, titled "Node Express Tutorial 21 = How to set up cookies and sessions ...", provides additional insights and in-depth exploration related to the topics discussed in this post.

Play video

In this video I show how to use the library express-session to set up a user session when they log in and then show how to set up an use a ...