I'm trying to implement a /login
and /logout
routes as well as some protected routes. However, after logging in, it seems like the log in isn't saved. When I ping /login
it adds the user to a session, but by the time I try to access a protected route, the session and user are gone. I got the examples to work, but my repo based off of them just doesn't. Every protected route always returns forbidden and even the logout route says the current_user
is None
.
I inserted tracing everywhere to figure out what was happening and noticed that between each request a new session is created. I don't know if this is what's supposed to happen, but all of the data stored in the session is lost.
My SessionLayer
is built with a MemoryStore
and my AuthLayer
is built with an SqliteStore
. I'm using sqlx
and sqlite as a database. I made sure my users are stored in a table called users
in my database.
If it helps, here's my setup for the layers:
let session_store = MemoryStore::new();
let session_layer = SessionLayer::new(session_store, &config.session_secret);
let user_store =
SqliteStore::<User, Role>::new(SqlitePool::connect(&config.database_file).await?);
let auth_layer = AuthLayer::new(user_store, &config.session_secret);
config.session_secret
is a 64 byte array, and config.database_file
is the path to my sqlite db.
type RequireAuth = RequireAuthorizationLayer<User, Role>;
let app = Router::new()
.route(
"/lists",
get(handlers::fetch_user_lists).layer(RequireAuth::login()),
)
.route(
"/list",
get(handlers::fetch_list).layer(RequireAuth::login()),
)
.route(
"/get-lists",
get(handlers::get_lists).layer(RequireAuth::login_with_role(Role::Admin..)),
)
.route(
"/get-users",
get(handlers::get_users).layer(RequireAuth::login_with_role(Role::Admin..)),
)
.route("/status", get(|| async { StatusCode::OK }))
.route("/login", get(handlers::login))
.route("/logout", get(handlers::logout))
.layer(auth_layer)
.layer(session_layer)
.with_state(app_state);
The app_state
contains a sqlx pool for the database. The enum Role
type looks like:
#[derive(
Debug, Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, sqlx::Type,
)]
#[repr(i32)]
pub enum Role {
/// Regular user access
#[default]
User = 0,
/// Full access to the database
Admin = 1,
}
I'm new to axum-login
, so sorry for the information dump, I just wanted to put anything that might be useful.
The /login
route takes a json body which describes the user to log into, a name and password. The handler looks like this:
pub type AuthContext = axum_login::extractors::AuthContext<User, SqliteStore<User, Role>, Role>;
#[debug_handler]
pub async fn login(
State(app): State<AppState>,
mut auth: AuthContext,
Json(user): Json<LoginUser>,
) -> AppResult<()> {
debug!("logging in user: {user:?}");
let user: User = sqlx::query_as("SELECT * FROM users WHERE name = ? AND password_hash = ?")
.bind(&user.name)
.bind(&user.password_hash)
.fetch_one(&mut app.conn.acquire().await?)
.await?;
info!("user: {user:?}");
auth.login(&user).await?;
Ok(())
}
Inside the /login
handler I can check auth.current_user
and it is set correctly, but as soon as another route is called that is gone. For example on one run of the server:
Directly before calling auth.login(&user).await?
Session {
id: "jGomHmRqscfQGcLIpZPLzQvN8lDqlQ5qQJMA0GHlamQ=",
expiry: Some(2023-02-10T02:20:06.301437200Z),
data: RwLock {
data: {},
poisoned: false,
..
},
cookie_value: Some("A/MJXP4FMV+rWiRmZMg0X5frpm9Gzt8dqSBCDW/YdVwroQTlmyocXT+X5FnMM5te4Y4PMnKmUwJT5kpBCXa+oQ=="),
data_changed: false,
destroy: false
}
Directly after:
Session {
id: "jGomHmRqscfQGcLIpZPLzQvN8lDqlQ5qQJMA0GHlamQ=",
expiry: Some(2023-02-10T02:20:06.301437200Z),
data: RwLock {
data: {
"_user_id": "\"3\"",
"_auth_id": "\"0qL/ZztGgEixRWFrMorDFNUsauceo4vmDcHcohY18PLWUUxTVc0/0SSSYMYeAaut45ZgEXbPJa1xeQBpyKP86w==\""
},
poisoned: false,
..
},
cookie_value: Some("A/MJXP4FMV+rWiRmZMg0X5frpm9Gzt8dqSBCDW/YdVwroQTlmyocXT+X5FnMM5te4Y4PMnKmUwJT5kpBCXa+oQ=="),
data_changed: true,
destroy: false
}
You can see that the user_id
has been added to the session. But, on the next request to /lists
(or any route, it's all the same) we have:
Session {
id: "txK5yTK9y5Hyu6wxcSF6SwK079pmdSI7n/YV7RPg/HQ=",
expiry: Some(2023-02-10T02:20:13.404553800Z),
data: RwLock {
data: {},
poisoned: false,
..
},
cookie_value: Some("29tPyCptmKMlBSJ8D08lYs1zFdz7NXaHouUiSt6u1o9JZvCI/OzmvZntzUSB6iMqFfI37YCkmKqZaKjQ8DsXjg=="),
data_changed: false,
destroy: false
}
All of a sudden we have a brand new session with the old data gone. I've been struggling for hours trying to find out what I'm doing wrong, and at this point I'm pretty sure it's just some tiny thing I'm missing. Or of course, I'm just misunderstanding how I should be using the library. If so, please let me know. I'd really appreciate if anyone could take a look and see if they notice anything out of order. Also, if you want more information just let me know. Thanks in advance