http-rs / async-session Goto Github PK
View Code? Open in Web Editor NEWAsync session support with plugabble backends
Home Page: https://docs.rs/async-session
License: Apache License 2.0
Async session support with plugabble backends
Home Page: https://docs.rs/async-session
License: Apache License 2.0
Welcome!
I've recently gotten an opportunity to do first pet project in rust and decided on using axum which gives an example of using async-session:
// `MemoryStore` is just used as an example. Don't use this in production.
let store = MemoryStore::new();
https://github.com/tokio-rs/axum/blob/main/examples/oauth/src/main.rs#L42
The same warning is memory store itself
async-session/src/memory_store.rs
Line 10 in 93a11ae
Is there a reason why this should not be used in production if losing all sessions on application crash acceptable behavior and application is not clustered? If there is I would love to add it in documentation in pointed places for others to understand.
Also many kudos for your work! ๐ฅ
There's a security issue on x86 introduced in 0.3.7 and fixed in 1.2.0 with SSE2 optimisations. Aarch64 would also benefit with NEON being supported by blake3.
It seems when using the Session
with tide's session middleware, it's not possible to modify the session expiry because it is not wrapped in an Arc
(I think).
This can be a somewhat tricky thing to reason with because of the session lifecycle in a request (and how it is cloned), but testing reveals that there are two things that would prevent this:
Arc
so the cloned session (via tide session middleware's handler) does not update the session expirySession::expire_in
does not mark the session data_changed
, so unless the check is disabled in the middleware, changes to session expiry will not cause the session to be updated by the store even it 1 is fixedThese are relatively straightforwards things to resolve, but may have some more subtle implications since it will change how sessions behave and existing code may be out there which depends on these quirks.
Hi, how do you setup async-session-redis as layer in Axum so that connection can be used in handlers?
At the moment I have session_layer but when I insert a record in Redis in one handler, I'm unable to retrieve it in another handler from the session.
Specifically how do you add the Redis store as a layer in Axum?
Framework is Axum. Thanks for any pointers or example.
A few features are missing from the Session api:
1. Session key renewal
Two of the most common events in a web application where there are "privilege-level changes" are login and logout, but there are others. Each of these events requires the Session ID be renewed. With this given, the Session API requires renewal functionality so that one may renew the session key on-demand. Although the Session ID changes, state is preserved.
2. Session purge
Purging involves removing the session state AND the cookie (ie "purge"). Purging is done during the logout process, for instance.
If you'd like to see an example implementation, such functionality exists in actix-web and an example useful for integration testing can be found here.
Why the cookie_value
is not cloned or serialized, I can't get the point of doing this. Can someone explain why we should have this field, and make it not cloned or serialized. Thanks a lot. ๐
It seems that the removal of async-std in v3 has broken the use of async-session with tide. If you try a simple tide program like the one in the async-mongodb-session/examples but with async-sqlx-session v0.4.0 you get build errors like
the trait `SessionStore` is not implemented for `SqliteSessionStore`
Moving async-sqlx-session back to async-session v2.0.1 from v3.0.0 makes the build error go away. I think this is a bug in async-session not async-sqlx-session because moving async-mongodb-session to async-session v3.0.0 from v2.0.1 results in the same build errors but for MongodbSessionStore, and I just don't see anything else in the latest async-sqlx-session that seems like it could be the cause.
Thoughts? Anything more I can do to help troubleshoot?
Hi,
First of all, thanks for creating such simple and convenient library.
I would like to check that if we call set_cookie_value
it should regenerate the id right?
https://github.com/http-rs/async-session/blob/main/src/session.rs#L341-L343
Regards,
Hi folks, it looks like the last published release was in May of 2021.
I'm curious if the maintainers could let us know if we should expect future releases or if projects utilizing this crate should instead publish their own releases?
Hey,
I'm trying to write a custom MemoryStore
that works with a sqlx
postgres database to store sessions for axum-login
.
I've got the following SQL, which has space for all the non-serde-skipped parts of Session
:
CREATE TABLE auth_sessions (
id TEXT PRIMARY KEY NOT NULL,
expiry TIMESTAMP
);
CREATE TABLE auth_session_data (
k TEXT NOT NULL,
v TEXT NOT NULL,
session_id TEXT NOT NULL,
CONSTRAINT fk_session_id
FOREIGN KEY (session_id)
REFERENCES auth_sessions(id)
ON DELETE CASCADE
)
Unfortunately, I can't work out a way to get the session contents to/from SQL, as I can't seem to find a constructor for making a Session
, or getting all of the data - I can get values from keys but if I don't know what keys are being used (and would rather not have to hardcode a list from axum-login
), and I can make a new cookie and add data but I can't do things like set the ID.
Hi I am writing a PostgresStore for this, I wonder if I just dump the whole session into Postgres or there are more preferable approach?
Something like this:
serde_json::to_string(&session).ok()
Are there any plans to release a new version? Lot's of dependencies moved and some crates that use async-session
need to make the upgrade and it would be much cleaner this way.
I've tried using MemoryStore
, but when I try to load the session and then alter it and finally save it the result of the store_session
call is None
, even though the session is not empty.
The way I handle it is as follows:
let jar = match session_cookie {
Some(session_cookie) => jar.add(Cookie::build(SESSION_COOKIE_NAME, session_cookie).finish()),
None => jar.remove(Cookie::named(SESSION_COOKIE_NAME)),
};
Apparently, this crate expects me to do this instead:
let jar = match session_cookie {
Some(session_cookie) => jar.add(Cookie::build(SESSION_COOKIE_NAME, session_cookie).finish()),
None => { /* do nothing */ },
};
The problem with this API is that the library does not provide itself control over how the cookie actually has to be set. Meaning, the library must be aware that some cookie (either the whole set of values, encoded somehow, or a key into the database with the sessions) must be set, and at some point, that cookie has to be updated (almost never when the id is stored, and a lot when the session value is stored) or removed (in both cases, when the session is destroyed). The library can thus expect to return the effective cookie value for the server to set on the client; the server handling the cookie can then check if the cookie has to be set.
I propose that we change the API as follows:
async fn store_session(...) -> Result<CookieAction>;
enum CookieAction {
/// Set the cookie with this new value.
Set(String),
/// Remove the cookie.
Remove,
/// No action is required (the cookie value should not change).
NoAction,
}
Or at least remap the semantics of what None
encodes currently to make it so it means "remove the cookie value on the client side".
Without this change, it seems I wouldn't be able to swap between the Memory and Cookie stores - as I'd have to knowledgeably choose whether to set the cookie or not depending on the backend I use. I know I'd have to set the cookie every time the session values change when using the CookieStore
backend.
Unfortunately chrono is still unpatched for a security advisory: chronotope/chrono#499 so it would be good to replace it, as this starts triggering folks from removing tide.
This was already mentioned in #40 but I think it still applies. From looking at the code, it seems that the cookie is just the serialized Session
instance and as such could easily be modified. Any signing has to be done externally, by middleware.
This means that the middleware has to be aware of the SessionStore
implementation and whether the data needs to be protected or not. In the case of axum-sessions
they always HMAC the cookie value, which is unnecessary given that the cookie value is just random data for most store implementations.
The docs should probably make it clear that there is no signing of the value in this crate. It would be possible for CookieStore
to handle signing (and possibly encryption) of the cookie value though, which would (optionally) remove the responsibility from the various middleware implementations. I could try and put together a PR if this makes sense?
I am writing some web server using axum library where I use multiple traits and put them in request extensions by converting them to Arc so that I don't have to make every handler accept generic parameters. Unfortunately I can't do that for SessionStore (which I use in a middleware for checking authentication) since it requires implementor to also implement Clone, making it non object safe. Is there a possibility to remove Clone requirement in next major version?
Example with handlers accepting generic parameters:
fn configure<U, T, S>(user_service: U, transaction_service: T, session_store: S) -> Router
where
U: UserService,
T: TransactionService ,
S: SessionStore
{
Router::new()
.route("/", handlers::index::<U>)
.route("/user", handlers::user::overview::<U, T, S>)
.layer(Extension(user_service))
.layer(Extension(transaction_service))
.layer(Extension(session_store))
}
// in handlers/index.rs
async fn index<U: UserService>(Extension(service): Extension<U>) -> impl IntoResponse {
//some stuff
}
// in handlers/user.rs
async fn overview<U: UserService, T: TransactionService, S: SessionStore>(
Extension(user_service): Extension<U>,
Extension(transaction_service): Extension<T>,
Extension(session_store): Extension<S>,
) -> impl IntoResponse {
//some stuff
}
And new way which I would be able to do if SessionStore is object safe:
fn configure<U, T, S>(user_service: U, transaction_service: T, session_store: S) -> Router
where
U: UserService,
T: TransactionService ,
S: SessionStore
{
Router::new()
.route("/", handlers::index)
.route("/user", handlers::user::overview)
.layer(Extension(Arc::new(user_service) as Arc<dyn UserService>))
.layer(Extension(Arc::new(transaction_service) as Arc<dyn TransactionService>))
.layer(Extension(Arc::new(session_store) as Arc<dyn SessionStore>)) // Not object safe, this will fail
}
// in handlers/index.rs
async fn index(Extension(service): Extension<Arc<dyn UserService>>) -> impl IntoResponse {
//some stuff
}
// in handlers/user.rs
async fn overview(
Extension(user_service): Extension<Arc<dyn UserService>>,
Extension(transaction_service): Extension<Arc<dyn TransactionService>>,
Extension(session_store): Extension<Arc<dyn SessionStore>>,
) -> impl IntoResponse {
//some stuff
}
This will make function signature smaller when I start adding more traits.
The sha2 and hmac dependencies look unused to me. Is there a reason for these dependencies?
Hello,
As the title says, async-redis-session is effectively dead, but it's recommended in the readme. There hasn't been activity on the repo since 2021, and not much heard from the author since 2022. The crate doesn't build on modern rust, and doesn't work with modern Redis.
It may be better to recommend https://github.com/brkp/async-fred-session instead? It isn't ideal either, fred has a sluggish upstream too, but it does work.
I am using ONLY async-sessions in Axum with the exact example from the docs as follows which throws the errors listed below:
Is there ANY chance you could get the example in your docs working without all the errors below.
use async_session::{Session, SessionStore, MemoryStore};
let mut store = MemoryStore::new();
// Create a new session.
let mut session = Session::new();
session.insert("user_id", 1)?;
assert!(session.data_changed());
// retrieve the cookie value to store in a session cookie
let cookie_value = store.store_session(session).await.unwrap()?;
// Retrieve the session using the cookie.
let session = store.load_session(cookie_value).await.unwrap()?;
assert_eq!(session.get::<usize>("user_id").unwrap(), 1);
assert!(!session.data_changed());
error[E0599]: no method named `store_session` found for enum `Result` in the current scope
--> src/main.rs:91:30
|
91 | let cookie_value = store.store_session(session).await;
| ^^^^^^^^^^^^^ method not found in `Result<RedisSessionStore, redis::types::RedisError>`
error[E0599]: no method named `load_session` found for enum `Result` in the current scope
--> src/main.rs:92:29
|
92 | let mut session = store.load_session("test-cookie".parse().unwrap()).await.unwrap();
| ^^^^^^^^^^^^ method not found in `Result<RedisSessionStore, redis::types::RedisError>`
error[E0308]: mismatched types
--> src/main.rs:111:38
|
111 | let session = store.load_session(cookie_value).await.unwrap();
| ^^^^^^^^^^^^ expected struct `std::string::String`, found enum `std::option::Option`
|
= note: expected struct `std::string::String`
found enum `std::option::Option<std::string::String>`
error[E0599]: no method named `get` found for enum `std::option::Option` in the current scope
--> src/main.rs:112:24
|
112 | assert_eq!(session.get::<usize>("user_id").unwrap(), 1);
| ^^^ method not found in `std::option::Option<Session>`
error[E0599]: no method named `data_changed` found for enum `std::option::Option` in the current scope
--> src/main.rs:113:22
|
113 | assert!(!session.data_changed());
| ^^^^^^^^^^^^ method not found in `std::option::Option<Session>`
error[E0283]: type annotations needed
--> src/main.rs:106:13
|
106 | session.insert("user_id", 1);
| ^^^^^^ cannot infer type for type `{integer}`
Reading the code i couldn't figure out what calling Session::destroy
really does. The documentation currently reads:
mark this session for destruction. the actual session record is not destroyed until the end of this response cycle.
From my quick reading of the code I didn't find find any location in the code where the destroy flag is read and the Session is actually destroyed.
I wonder why wouldn't Session wrap around Arc
, but part of its is Arc
, such as data
.
In some cases I need to pass session around and mut
it.
Maybe there is a way that I haven't thought though, thanks.
A Session
has a data field of type HashMap<String, String>
. This forces a session to always use JSON for serialization/deserialization.
In my case I would prefer if it was a HashMap<String, Bytes>
so that I can use more efficient serde methods (e.g.: bincode, which this crate already uses)
Would it be possible to change Session
so that it becomes generic like Session<T>
so that data becomes HashMap<String, T>
?
I've been diving into session management in Tide. It seems like it is still a WIP but it sounded like this library is the direction things are moving towards.
I am noticing that Tide's cookie middleware hides the CookieJar from the consumer, so it can't be used with these traits. I'm not sure whether Tide should allow access to the CookieJar or if this should be changed to take a Request/Response pair instead of a CookieJar.
Any thoughts?
Hello,
In documentation it is written that the data in the cookie is only signed, but not encrypted. I understand that it is signed because the cookie id is an hash of the cookie data, and attempting to alter only the data will fail...
But since there is no (external) signing key, what would prevent someone to forge a cookie with any data for a website ?
If I understand correctly (and maybe I don't), that would be a severe security issue ?!
Would it be possible to pass a server known only key to the CookieStore::new(a_secret_key)
to sign all the cookies (and deactivate them all if the key is changed) ?
Thanks,
I was debugging mongo and couldn't find the creation date in the session store. We should probably track this for all sessions; even if not tracked by UTC it does help with debugging. Perhaps the individual backends provide primitives for this as well that we can use.
I was trying to find from this screenshot which sessions were created today. It doesn't say, heh.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.