Cookies in actix web

I'm trying to implement jwt auth with cookies rn

Using actix-web and actix-cors

I tried doing it through authorization header and it works perfectly
But as im trying to pass token through cookie it doesn't seem to appear in browser storage eg cookies.

pub async fn login(db: web::Data<PgPool>, input: web::Json<LoginForm>) -> impl Responder {
    let res = user_service::login(&db, input.0).await;

    match res {
        Ok(res) => {
            let cookie = Cookie::build("jwt_token", res.1)
                .http_only(true)
                .secure(false)
                .same_site(actix_web::cookie::SameSite::Strict)
                .finish();
            return HttpResponse::Ok()
                //.append_header(("Authorization", res.1))
                .cookie(cookie)
                .json(serde_json::json!(res.0))
        },
        Err(_err) => return Error::InternalError.error_response(),
    }
}

its just a draft so dont mine me using internal errors.
res is tuple (User, string)

Also from insomnia cookies work fine

From what browser gets i can clearly see my server does send cookie there

HTTP/1.1 200 OK
content-length: 106
access-control-allow-origin: http://localhost:5173
vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers
content-type: application/json
set-cookie: jwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX3V1aWQiOiI0MzA3ZmE5ZS1mZDg2LTRiNTYtYTlmNC1jOWFmYmRkZjU3MjQiLCJleHAiOjE3MzY1MDM5NTh9._ganyuZNnliMrIfAjLiIb0TyY1VVDDSqdwcAsSOYTYk; HttpOnly; SameSite=Strict; Path=/login; Domain=localhost
access-control-expose-headers: set-cookie, access-control-allow-origin, content-type
access-control-allow-credentials: true
date: Tue, 10 Dec 2024 10:13:38 GMT

But i don't know why it doesn't appear in storage.
I also tried to set http_only to false, still nothing

Is there something im missing?

actix-cors suggests that you are making cross-site requests? In that case, setting the SameSite attribute of your cookie to Strict will not send your cookie as part of cross-site requests. Have you tried using a cookie without setting its SameSite attribute?

1 Like

Yes i did, tried lax, none and strict. Nothing is changing. I'm testing it on localhost so maybe that's why it doesn't affect anything.

Cookie does appear in response as set-cookie, but it doesnt get stored in browser storage and i cant use them to send back to server

Ah, yes. You also need to set the secure flag of the cookie with .secure(true) for SameSite::None to take effect on cross-site requests. From the docs:

If the SameSite attribute is “None”, the cookie is sent in all cross-site requests if the “Secure” flag is also set, otherwise the cookie is ignored.

1 Like

I used same site strict and server did send cookie to frontend, but it didn't get stored. Also with strict i was using Secure set on false

If im using same site strict on cookie and using cross site requests frontend won't pass them? Or server won't pass them to frontend?

Either way my only issue with cookies is they don't get stored in the browser. I tested it in firefox and chromium. Using utilities like insomnia all works perfectly fine. Cookies are set and they apply to any request after it

Maybe browser is cause of my problem? Is there any additional configuration to store cookie? I thought they do that automatically, but anyways. On frontend i'm using sveltekit if it is important

1 Like

Yes, the browser is probably your problem, not storing the cookie because it is not properly configured. With CORS it's the same, it's just two header fields that are respected by all mayor browser engines, they don't alter the behaviour of HTTP itself. Have you tried this to create your cookie?

            let cookie = Cookie::build("jwt_token", res.1)
                .http_only(true)
                .secure(true)
                .same_site(actix_web::cookie::SameSite::None)
                .finish();
1 Like

If that's a request to another origin, the situation is hopeless and cookies basically won't work.

This is how ads track and profile people, so browsers have either completely blocked them, or isolated them to per-origin sandbox, or have very limited unreliable heuristics for special cases.

If you first navigate to cookie's domain as the top level page, perform user action such as mouse click, then perform POST to the cookie's origin, and then redirect to another origin, it might survive. But don't count on it.

1 Like

Does localhost:5173 -> localhost:8080 counts as different origins? i thought as long as im just testing on local all should be fine

Omg that was soooo bad

Solution: I had to put credentials: 'include' to my login call in frontend.
So basically problem was in frontend while i was trying to fix backend

const login_user = async (email, password) => {
    try {
        const res = await fetch("http://localhost:8080/api/users/login", {
            method: "POST",
            credentials: 'include',
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                email,
                password,
            })
        });
        const logged_user = await res.json();

        const token = res.headers.get("Authorization");

        localStorage.setItem('jwt_token', token);
        return logged_user;
    } catch (e) {
        console.error(e);
        return e;
    }
}

That's how it looks after fix

I thought this attribute stands for sending cookie as "credentials" to server and not vise versa.

Anyways your thoughts helped me a lot. Thanks for your time. Really appreciate it

1 Like