Code for validating session_tokens

Hello

I have two tables: users and books. Books contains a column user_id referencing users.id as foreign key. users also contains a column session_token in which a newly generated token is saved each time the user authenticates.

Now I have two applications: A NodeJS app where the user can register/login and perform certain actions calling my Rust-app. The Rust-app is responsible for manipulating data in the database. Both have access to the database.

Because the user authenticates in NodeJS, the Rust-app does not have direct access to the session_id and thus if someone calls GET rust-example.com/books/1 it can't by default know if book with id one belongs to the user making the request.

This is why I send the session_token as header from NodeJS to Rust. That way Rust can retrieve the session_token from the HTTP header and check if the session_token in the header matches the session_token in the database-table for the user to which book with id 1 belongs to.

This is my function to check if the session_token in the request belongs to the user to which a resource (e.g book) belongs to:

// Function to check if requested resource belongs to authenticated user.
#[derive(PartialEq)]
pub enum DBTable<'a> {
    Book(&'a str),
}

async fn validate_session_token(session_token: &str, table: DBTable<'_>, psql: Psql) -> Result<bool, String> {
    // Retrieve user with session token.
    let query = psql.lock().await
       .query("
            SELECT
                id
            FROM 
                users
            WHERE
                session_token = $1
        ", &[&session_token]).await;

    let user_id : i32 = match query {
        Ok(q) => {
            if q.len() == 0 {
                return Err("No user found.".into());
            }

            q[0].get("id")
        },
        Err(e) => {
           return Err(format!("Error: {}", e));
        }
    };

    // Check if user_id is foreign key of DBTable.
    let (table_name, id) = match table {
        DBTable::Book(id) => ("books", id),
        _ => {
            return Err("Invalid table given as parameter.".into());
        }
    };
    
    let sql = format!("SELECT * FROM {table} WHERE id = $1 AND user_id = $2", table = table_name);
    let query = psql.lock().await.query(&*sql, &[&id, &user_id]).await;

    match query {
        Ok(q) => {
            return Ok(q.len() > 0);
        },
        Err(e) => {
           return Err(format!("Error: {}", e));
        }
    }
}

It is called like this:

        // Check if book belongs to user doing request.
        match req.headers.get("session_token") {
            Some(token) => {
                match validate_session_token(token, DBTable::Book(&*book_id), psql.clone()).await {
                    Ok(v) => {
                        if !v {
                            return Ok(Routes::unauthorized().await);
                        }
                    },
                    Err(_) => {
                        return Ok(Routes::internal_server_error().await);
                    }
                }
            },
            None => {
                return Ok(Routes::unauthorized().await);
            }
        }

Is this code okay? I am using &str instead of String as parameter type for the function because I feel I use String to much. I should only use String if the string needs to be mutable IMHO. In this case it are just ID's I pass to the function so &str seems better.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.