axum-flash
axum-flash copied to clipboard
Seeing inconsistent behaviour for different routes
Hi, I'm encountering odd behaviour when flashing messages for different routes in my Axum server and I'm not quite sure what the issue is.
For example, I have these routes for registering new users (under /register):
pub async fn get_register(flash: IncomingFlashes, Extension(tera): Extension<Tera>) -> Html<String> {
let mut context = tera::Context::new();
if flash.len() == 1{
context.insert("message", flash.iter().nth(0).unwrap().1);
}
Html(tera.render("register.html.tera", &context).unwrap())
}
pub async fn post_register(
form: Form<Register>,
mut flash: Flash,
DatabaseConnection(conn): DatabaseConnection,
) -> Redirect {
// Try to register the user
// [......]
Ok(_) => Redirect::to("/login"),
Err(e) => {
flash.error("Unable to register user");
Redirect::to("/register")
}
}
The flashing functionality works as expected for these routes (i.e. if there's an error during registration, the user is redirected and the flash message is shown. Upon refreshing the page, the flash message is cleared.
I then have some other routes (under /types/create) which are very similar to the above, but the flash message persists even after refreshing the page (and I can see the 'axum-flash' cookie stored in my browser after refreshing).
pub async fn get_create_type(flash: IncomingFlashes, Extension(tera): Extension<Tera>) -> Html<String>{
let mut context = tera::Context::new();
if flash.len() == 1{
context.insert("message", flash.iter().nth(0).unwrap().1);
}
Html(tera.render("new_type.html.tera", &context).unwrap())
}
pub async fn post_create_type(
form: Form<PostNewType>,
mut flash: Flash,
DatabaseConnection(conn): DatabaseConnection,
) -> Redirect {
// Logic to create a new type
// If a type with that name exists already, do:
flash.error("Type already exists");
Redirect::to("/types/create")
}
In these other routes, I've tried adding extra code to forcibly remove the 'axum-flash' cookie (I also tried using the other 2 types of CookieJar as well), but it still persists:
pub async fn get_create_type(flash: IncomingFlashes, jar: CookieJar, Extension(tera): Extension<Tera>) -> impl IntoResponse{
let mut context = tera::Context::new();
if flash.len() == 1{
context.insert("message", flash.iter().nth(0).unwrap().1);
}
// Attempt to remove the 'axum-flash' cookie
let updated_jar: CookieJar = jar.remove(Cookie::named("axum-flash"));
(updated_jar, Html(tera.render("new_type.html.tera", &context).unwrap()))
}
From this page (/types/create) with the persistent cookie, if I then navigate back to the /register route and refresh the page, the cookie is cleared.
Can you think of any reason why in some instances the flash cookie is cleared, but in other cases it's not? Let me know if you need more details on anything
Hm that sounds pretty weird. Not sure what's going on.
Are you able to make a minimal example that I can cargo run?
I've uploaded a minimal example here: https://github.com/66OJ66/flash_testing
I think the error is related to paths with multiple segments i.e. when creating this example, using /do_a_thing as the route didn't cause any issues, but using /do/a/thing yields the error I mentioned above. Possibly using SameSite = Strict in the flash cookie is causing this issue?
Hi, are you able to reproduce the example on your end using the code I linked to above?
I haven't had time to look more into it. Did you get it working?
Getting the exact same issue here. My login route is /accounts/login and the flash cookie does not seem to be cleared properly, so the flash message stays there forever. It's also visible in the Firefox session storage:

Changing the login route to /login makes it work perfectly, and the cookie disappears before I even have the time to look at it.
Update: I've found a consistent workaround for the problem, by manually clearing the cookie:
async fn your_route_here(
cookies: Cookies,
flashes: IncomingFlashes,
) -> impl IntoResponse {
//TODO use `flashes` here
//Manually delete the cookie
let mut flash_killer = Cookie::named("axum-flash");
flash_killer.make_removal();
flash_killer.set_path("/");
cookies.add(flash_killer);
//TODO return your response here
}
You need to use the Cookies extractor, not CookieJar (since the former overrides the latter).
Also note there is a subtle difference between CookieJar::remove() and Cookie::make_removal(). The former doesn't actually delete the cookie, it just removes it from the server side cookie jar. You need to explicitly overwrite the cookie to modify/delete it on the client side.
~~Also ensure you've got the CookieManagerLayer added to your middleware stack, in addition to the axum_flash::layer. Even if you call with_cookie_manager(), the cookie manager will not be available to your own routes if you don't explicitly add it. For example:~~ Update: this is caused by having a version mismatch between tower-cookies 0.7.0 and 0.6.0 (the latter version is used by axum-flash 0.4.0). Downgrade your version of tower-cookies to 0.6.0 to fix it.
Router::new()
.layer(
ServiceBuilder::new()
.layer(axum_flash::layer(cookie_key).with_cookie_manager())
.layer(CookieManagerLayer::new())
);
You need to use the Cookies extractor, not CookieJar (since the former overrides the latter).
You mean CookieJar from axum-extra? Yes those two are probably conflicting. I really should convert this library use axum-extra's CookieJar.
Also ensure you've got the CookieManagerLayer added to your middleware stack, in addition to the axum_flash::layer. Even if you call with_cookie_manager(), the cookie manager will not be available to your own routes if you don't explicitly add it. For example:
That doesn't sound right to me. The CookieManagerLayer is pushed here when you call with_cookie_manager so need need to add it twice. There must be something else going on.
Whoops didn't mean to close this.
I would encourage you to pull this repo down and debug it. I dunno when I'll have time to look into it.
@davidpdrsn If I don't explicitly add the CookieManagerLayer to my layer stack, I get this error...
Can't extract cookies. Is CookieManagerLayer enabled?
...in the your_route_here I defined above: https://github.com/davidpdrsn/axum-flash/issues/4#issuecomment-1170012587. Even if I call .with_cookie_manager() on the axum_flash::layer.
Maybe it has something to do with the order of the layers?
Maybe it has something to do with the order of the layers?
Not sure 🤷
@davidpdrsn Why did you close this issue?
Huh I didn't do that knowingly.
Lol either something happened on github or my computer. When I press command+enter to submit a comment it hits the "Close with comment" button and not the "Comment" button 🤦
Can't extract cookies. Is CookieManagerLayer enabled?
I just got this error and it was caused by two incompatible tower-cookies versions.
@ssendev Thank you, that turned out to be the issue! axum-flash 0.4.0 uses tower-cookies 0.6.0, even though 0.7.0 is available. I'll update my workaround.
Oh I didn't realize. Are you up for submitting a PR that updates axum-flash?
I've published axum-flash 0.5 which users tower-cookies 0.7.