imbolc/tower-cookies

Example of session while using tower_cookies and axum?

SylvainMartel opened this issue · 6 comments

See tokio-rs/axum#695

Hi, when using the flash crate in axum, we also have to use the crate tower_cookies too for cookie management. As such, the session example in the axum repo will not work(I remember reading somewhere that tower_cookies kinda override any cookies made by other means?).

Right now, I have no idea how to get current cookies while inside axum FromRequest implementation. When I do a cookies.list()) in the FromRequest implementation, it shows an Empty list even though there are 2 cookies that were created upon the user logging in(I can see them correctly in the browser.)

Having an example of session using tower-cookies would be much appreciated :)

Hey :) If you see cookies in the browser, cookies.list() should certainly work. Check if middlewares are initialized correctly and are in a meaningful order. Check if you can see these cookies using extractor: async fn handler(cookies: Cookies). If it won't help you can simplify and share your code and I'll check it.

Hi, thanks for the reply. Yes, I can see and grab the cookies normally inside a handler, but not inside a "FromRequest" implementation. I'm no dev, so it might be something trivial that I am missing. :)

This simple route works fine:

async fn greet(
    extract::Path(name): extract::Path<String>,
    cookies: Cookies,
) -> HelloTemplate {
    dbg!(&cookies.list());  //<<<<<- The cookie is seen correctly
    let template = HelloTemplate { name };
    template
}

But if I want to see the cookies through another FromRequest (like the jwt example,or the session example) then I cannot see the cookie from inside that implementation:


async fn greet(
    extract::Path(name): extract::Path<String>,
    cookies: Cookies,
    claim: Claims, //<<<<<<<<<<<<<------- This verifies the jwt token, but since it's inside a cookie I need to get the cookies too
) -> HelloTemplate {
    let template = HelloTemplate { name };
    template
}

pub struct Claims {
    pub sub: String,
    pub role: String,
    pub exp: i64,
}

impl Display for Claims {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Email: {}\nRole: {}", self.sub, self.role)
    }
}

#[async_trait]
impl<B> FromRequest<B> for Claims
where
    B: Send,
{
    type Rejection = AuthError;

    async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
        let cookies = Cookies::default(); 
        dbg!(&cookies.list());  // <<<<<<< ---  Empty

       //code to extract token and verify it

        Ok(token_data.claims)
    }
}

and the loading order:

    let app = Router::new()
   //snip all trhe routes
        .layer(
            ServiceBuilder::new()
                .layer(CookieManagerLayer::new())
                .layer(AddExtensionLayer::new(shared_state))
                .layer(axum_flash::layer(key).with_cookie_manager()),
        );

To get an extension inside the FromRequest, we usually have to use something like let Extension(user_state) = Extension::<Arc<UserState>>::from_request(req) but I cannot find a way to make it work with CookieManagerLayer.

I'd say it's rather courageous to use rust not being a dev 🙇

The problem is in Cookies::default() - it knows nothing about headers, so it just creates a new empty jar. I've added an example how to use already initialized one instead: https://github.com/imbolc/tower-cookies/blob/main/examples/counter-extractor.rs#L24-L33

That did it :) Huge thanks! I learned a new way to call extensions too(and indeed, reading the RequestParts doc clearly shows it's part of that struct.)

Yes, Rust can be a bit daunting for a hobbyist like me, but somehow, it's what click the most for me syntax wise. Before moving to pure electronic and then IT, I learned basic programming in college in the mid-90's with Quick Basic, Assembly and C, and Rust feels just "right" for me with that background. What I lack is all the years of experience devs have reading API docs, or all the coding patterns you learn as you go on. I did a small gym software for a friend in python(django) and was looking at another language to make it faster, so I tired GO, but I simply cannot wrap my head around the signature of a function in GO. The brain refuses to compute it :P So Rust it is. Even with the struggles, I like it alot, and the fact that there isn't really any fully mature framework yet forces me to learn what's under the hood. I just wish there was a complete book on what "subsystems" you have to make for a modern web app. The closest I have found so far is "Let's Go" by Alex Edwards.

But enough babbling, time to go extract the info from that cookie :D

I'd agree completely, Rust seem to be the best mainstream language right now. The learning curve could be quite overwhelming though :) But after I lower my anticipations and take my time, it always feels more than rewarding.

Have you seen this book: https://www.zero2prod.com/

Have you seen this book: https://www.zero2prod.com/

I just pulled the triggeron that one and finally got it. Chapter 10 is about session so it is indeed a good time to get it :)