#Axum session_token cookie help

17 messages · Page 1 of 1 (latest)

vernal knot
#

I have a login function that checks the (username, password), generates a session token and sets the response coookie. but i have an error. pls help.
also, please feel free to suggest any improvements

#
use std::time::Duration;

use axum::{
    http::{header::USER_AGENT, HeaderMap, StatusCode},
    Extension, Json,
};
use axum_extra::extract::{
    cookie::{Cookie, SameSite},
    CookieJar,
};
use axum_macros::debug_handler;
use bcrypt::verify;
use serde::Deserialize;
use sqlx::{types::chrono::Utc, SqlitePool};
use uuid::Uuid;

use crate::error::{AppError, AuthError};
#
#[derive(Deserialize)]
pub struct Login {
    pub username: String,
    pub password: String,
}

#[debug_handler]
pub async fn login(
    Extension(pool): Extension<SqlitePool>,
    headers: HeaderMap,
    jar: CookieJar,
    Json(login): Json<Login>,
) -> Result<(CookieJar, StatusCode), AppError> {
    struct User {
        id: i64,
        password_hash: String,
    }

    let user = sqlx::query_as!(
        User,
        r#"SELECT id as "id!", password_hash FROM users WHERE username = ?"#,
        login.username
    )
    .fetch_one(&pool)
    .await
    .map_err(|e| match e {
        sqlx::Error::RowNotFound => AuthError::UserNotFound(login.username.clone()).into(),
        _ => <sqlx::Error as Into<AppError>>::into(e),
    })?;

    match verify(login.password, &user.password_hash)? {
        false => Err(AuthError::InvalidCredentials.into()),
        true => {
            let session_token = Uuid::new_v4().to_string();
            let created_at = Utc::now();
            let expires_at = created_at + Duration::from_secs(3600);
            let user_agent = headers.get(USER_AGENT).and_then(|val| val.to_str().ok());

            sqlx::query!(
                "INSERT INTO sessions (token, user_id, created_at, expires_at, user_agent) VALUES (?, ?, ?, ?, ?)",
                session_token,
                user.id,
                created_at,
                expires_at,
                user_agent
            )
            .execute(&pool)
            .await?;

            let session_cookie = Cookie::build(("session_token", session_token))
                .path("/")
                .http_only(true)
                .same_site(SameSite::Strict)
                .expires(expires_at)
                ;
            let jar = jar.add(session_cookie);

            Ok((jar, StatusCode::OK))
        }
    }
}
#
error[E0277]: the trait bound `std::option::Option<time::offset_date_time::OffsetDateTime>: From<sqlx::types::chrono::DateTime<Utc>>` is not satisfied
  --> src\login.rs:72:26
   |
72 |        .expires(expires_at)
   |         ------- ^^^^^^^^^^ the trait `From<sqlx::types::chrono::DateTime<Utc>>` is not implemented for `std::option::Option<time::offset_date_time::OffsetDateTime>`, which is required by `sqlx::types::chrono::DateTime<Utc>: Into<Expiration>`
   |                  |
   |                  required by a bound introduced by this call
   |
   = help: the following other types implement trait `From<T>`:
             <std::option::Option<&'a Id> as From<&'a EnteredSpan>>
             <std::option::Option<&'a Id> as From<&'a Span>>
             <std::option::Option<&'a Id> as From<&'a tracing_core::span::Current>>
             <std::option::Option<&'a T> as From<&'a std::option::Option<T>>>
             <std::option::Option<&'a mut T> as From<&'a mut std::option::Option<T>>>
             <std::option::Option<&'static tracing::Metadata<'static>> as From<&'a tracing_core::span::Current>>
             <std::option::Option<Id> as From<&'a EnteredSpan>>
             <std::option::Option<Id> as From<&'a Id>>
           and 19 others
   = note: required for `sqlx::types::chrono::DateTime<Utc>` to implement `Into<std::option::Option<time::offset_date_time::OffsetDateTime>>`
   = note: required for `Expiration` to implement `From<sqlx::types::chrono::DateTime<Utc>>`
   = note: 1 redundant requirement hidden
   = note: required for `sqlx::types::chrono::DateTime<Utc>` to implement `Into<Expiration>`
note: required by a bound in `cookie::builder::CookieBuilder::<'c>::expires`
  --> C:\Users\zahas\.cargo\registry\src\index.crates.io-6f17d22bba15001f\cookie-0.18.1\src\builder.rs:88:23
   |
88 |     pub fn expires<E: Into<Expiration>>(mut self, when: E) -> Self {
   |                       ^^^^^^^^^^^^^^^^ required by this bound in `CookieBuilder::<'c>::expires`
#
[dependencies]
axum = "0.7"
axum-extra = { version = "0.9", features = ["cookie"] }
axum-macros = "0.4"
bcrypt = "0.15"
dotenv = "0.15"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
sqlx = { version = "0.8", features = [
    "chrono",
    "migrate",
    "runtime-tokio-native-tls",
    "sqlite",
] }
thiserror = "1"
tokio = { version = "1", features = ["full"] }
tower-http = { version = "0.5", features = ["trace"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
uuid = { version = "1.2", features = ["v4"] }
zealous socket
#

@vernal knot you need to use the time crate instead of chrono. also if you're doing sessions, i would use tower-sessions and that handles it all for you. once the user is authenticated you can add like logged_in = {user_id} to the session store

#

?crate tower-sessions

crystal zenithBOT
#

🥠 Sessions as a tower and axum middleware.

Version

0.13.0

Downloads

273 463

vernal knot
zealous socket
vernal knot
zealous socket
#

the crate has some docs

#

and examples in the github

vernal knot
#

are you talking about custom extractors?

zealous socket
#

nah

#

1s