Continuing with our actix-web learning application, we will discuss proper AJAX calls to ensure reliable functionality of CORS and session cookies. This also addresses issue ❷ raised in a previous post.

🦀 Index of the Complete Series.

098-feature-image.png
Rust: actix-web CORS, Cookies and AJAX calls.

🚀 Please note, complete code for this post can be downloaded from GitHub with:

git clone -b v0.8.0 https://github.com/behai-nguyen/rust_web_01.git

The actix-web learning application mentioned above has been discussed in the following seven previous posts:

  1. Rust web application: MySQL server, sqlx, actix-web and tera.
  2. Rust: learning actix-web middleware 01.
  3. Rust: retrofit integration tests to an existing actix-web application.
  4. Rust: adding actix-session and actix-identity to an existing actix-web application.
  5. Rust: actix-web endpoints which accept both application/x-www-form-urlencoded and application/json content types.
  6. Rust: simple actix-web email-password login and request authentication using middleware.
  7. Rust: actix-web get SSL/HTTPS for localhost.

The code we’re developing in this post is a continuation of the code from the seventh post above. 🚀 To get the code of this seventh post, please use the following command:

git clone -b v0.7.0 https://github.com/behai-nguyen/rust_web_01.git

– Note the tag v0.7.0.

❶ We are not adding any new files to the project; it remains the same as in the seventh post. We are only making changes to a few modules.

.
├── ...
├── README.md ★
├── src
│ ├── auth_handlers.rs ★
│ ├── auth_middleware.rs ★
│ └── lib.rs ★
├── ...

– Please note, those marked with are updated, and those marked with are new.

❷ Session cookies.

I was working on CORS, session cookies, and AJAX calls when I realised that we couldn’t get session cookies to work consistently across domains for Firefox and other Chromium browsers without HTTPS. This realisation prompted the focus on enabling the application to run under HTTPS, as discussed in the seventh post: Rust: actix-web get SSL/HTTPS for localhost.

💥 However, despite running the application under HTTPS, we later discovered that it still didn’t fully resolve the cookie issue. This is because Chromium browsers are in the process of phasing out third-party cookies.

Cross-Origin Resource Sharing (CORS) allowed origin.

While studying and experimenting with this issue, I made an observation regarding the application’s allowed origin. The allowed origin is set to http://localhost, as per configuration.

During my experiments, I removed the http:// scheme, leaving only localhost as the allowed origin:

ALLOWED_ORIGIN=localhost

CORS just simply reject the requests:


● Please refer to the following Mdm Web Docs for explanations regarding
CORS header ‘Access-Control-Allow-Origin’ missing.

● Additionally, I found the Wikipedia article on Cross-origin resource sharing to be informative.

As for why I made that change, I can’t recall the exact reason. It may have been due to some confusion while reading the documentation and examining examples from other sources.

According to the same-origin policy, an origin is defined by its scheme, host, and port. You can find detailed rules for origin determination in the Wikipedia article on the Same-origin policy.

Two resources are considered to be of the same origin if and only if all these values are exactly the same.

https://en.wikipedia.org/wiki/Same-origin_policy#Origin_determination_rules

This would likely explain why dropping the scheme resulted in all requests being rejected

❹ Cookies and AJAX calls.

In the sixth post, we raised issue ❷. I further noted in that section:

It seems logical, but it does not work when we log in using JSON with either an invalid email or password. The client tools simply report that the request could not be completed. I haven’t been able to work out why yet.

By “...log in using JSON...” I mean AJAX calls. I do apologise for not being clear earlier.

– Recall that in this scenario, the application acts as an API-like server or a service.

After some study and experimentation, I have been able to determine the reasons:

  1. AJAX requests must have both xhrFields.withCredentials and crossDomain set to true.
  2. How session cookies are created. We will discuss these in detail in the following sections.

⓵ AJAX and cross domain.

I use the HTML page ajax_test.html to test the application with AJAX calls. In the sixth post, I used the function runAjaxEx(…), which caused session cookies not to work properly when calls were cross-domain. Now, I am using the function runAjaxCrossDomain(…):

...
            // https://stackoverflow.com/questions/76956593/how-to-persist-data-across-routes-using-actix-session-and-redisactorsessionstore
            xhrFields: {
                withCredentials: true
            },
            crossDomain: true,
...

Refer to the following Mdm Web Docs page on XMLHttpRequest: withCredentials property for explanations of xhrFields.withCredentials.

💥 Please note that I am still unclear why this is considered a cross-domain case. I am accessing ajax_test.html via localhost, while the application is hosted at localhost:5000. In the correct response screenshot below, without the cross-domain setting, the response would be the login HTML page without the Please check login detail. message because cookies are simply rejected:


We have successfully refactored the fn first_stage_login_error_response(request: &HttpRequest, message: &str) -> HttpResponse function, as discussed in the aforementioned issue ❷. Additionally, we’ve included a call to create the server-side per-request original-content-type cookie:

...
        .cookie(build_original_content_type_cookie(&request, request.content_type()))
...

⓶ How session cookies are created.

As mentioned earlier, without HTTPS, cookies do not function properly; they are rejected by Chromium browsers and Firefox.

In this post, the cookie implementations are as follows:

  1. Scheme: HTTPS://
  2. Secure: true
  3. SameSite: None

Let’s examine some examples where cookies are rejected.

1. Scheme: HTTP://, Secure: false, and SameSite: Strict.

When accessing ajax_test.html on localhost, and the application is hosted on the Ubuntu 22.10 machine at 192.168.0.16:5000, the server-side per-request cookies are rejected:


The expected output is:

{
	"code": 401,
	"message": "Please check login detail.",
	"session_id": null
}

The warnings in the above screenshot are:

Some cookies are misusing the recommended “SameSite“ attribute

Cookie “original-content-type” has been rejected because it is in a cross-site context and its “SameSite” is “Lax” or “Strict”.

Cookie “redirect-message” has been rejected because it is in a cross-site context and its “SameSite” is “Lax” or “Strict”.

Cookie “redirect-message” has been rejected because it is in a cross-site context and its “SameSite” is “Lax” or “Strict”.

Cookie “original-content-type” has been rejected because it is in a cross-site context and its “SameSite” is “Lax” or “Strict”.

Cookie “redirect-message” has been rejected because it is in a cross-site context and its “SameSite” is “Lax” or “Strict”.

Cookie “original-content-type” has been rejected because it is in a cross-site context and its “SameSite” is “Lax” or “Strict”.

The warnings indicate that both Lax and Strict would result in these cookies being rejected. The only remaining option left is None. For more information, please refer to the following Mdm Web Docs article on Set-Cookie SameSite.

2. Scheme: HTTP://, Secure: false, and SameSite: None.

The cookies are accepted, but there are still warnings regarding the server-side per-request cookies:


The warnings are:

Some cookies are misusing the recommended “SameSite“ attribute

Cookie “original-content-type” will be soon rejected because it has the “SameSite” attribute set to “None” without the “secure” attribute. To know more about the “SameSite“ attribute, read https://developer.mozilla.org/docs/Web/HTTP/Headers/Set-Cookie/SameSite

Cookie “redirect-message” will be soon rejected because it has the “SameSite” attribute set to “None” without the “secure” attribute. To know more about the “SameSite“ attribute, read https://developer.mozilla.org/docs/Web/HTTP/Headers/Set-Cookie/SameSite

Cookie “redirect-message” will be soon rejected because it has the “SameSite” attribute set to “None” without the “secure” attribute. To know more about the “SameSite“ attribute, read https://developer.mozilla.org/docs/Web/HTTP/Headers/Set-Cookie/SameSite

Cookie “original-content-type” will be soon rejected because it has the “SameSite” attribute set to “None” without the “secure” attribute. To know more about the “SameSite“ attribute, read https://developer.mozilla.org/docs/Web/HTTP/Headers/Set-Cookie/SameSite

Cookie “redirect-message” will be soon rejected because it has the “SameSite” attribute set to “None” without the “secure” attribute. To know more about the “SameSite“ attribute, read https://developer.mozilla.org/docs/Web/HTTP/Headers/Set-Cookie/SameSite login

Cookie “original-content-type” will be soon rejected because it has the “SameSite” attribute set to “None” without the “secure” attribute. To know more about the “SameSite“ attribute, read https://developer.mozilla.org/docs/Web/HTTP/Headers/Set-Cookie/SameSite

The application also maintains an application-wide publicly available cookie named authorization cookie, discussed toward the end of this section. This cookie stores the access token after a successful login. Based on the warnings above, we would expect to receive the same warning for this cookie. And indeed, we do:


Generally, this is not a problem, as this access token is also included in the response’s authorization header, clients can get it from this header instead.

3. Scheme: HTTP://, Secure: false, and SameSite: None – as per in 2.

Chromium browsers, including Opera, appear to reject cookies even when not accessed cross-domain. For instance, when logging in with an invalid email, the login page is displayed without the Please check login detail. message.


Now that the application can run under HTTPS://, let’s set Secure to true and SameSite to None and observe how browsers handle cookies.

Scheme: HTTPS://, Secure: true, and SameSite: None. We need to make two changes to the cookie creation code.

First, for server-side per-request cookies, we’ve already made changes since the seventh post. Please refer to src/helper/app_utils.rs:

...
pub fn build_cookie<'a>(
    _request: &'a HttpRequest,
    name: &'a str,
    value: &'a str,
    server_only: bool,
    removal: bool
) -> Cookie<'a> {
    ...

    let mut cookie = Cookie::build(name, value)
        // .domain(String::from(parts.collect::<Vec<&str>>()[0]))
        .path("/")
        .secure(true)
        .http_only(server_only)
        .same_site(SameSite::None)
        .finish();
...

I’ve also removed the domain setting, as it doesn’t seem to make any difference; we just let it use the default value.

These changes will also apply to the authorization cookie, discussed toward the end of this section.

Secondly, for the third-party secured cookie id, we update the src/lib.rs module as follows:

...
            .wrap(SessionMiddleware::builder(
                    redis_store.clone(),
                    secret_key.clone()
                )
                .cookie_secure(true)
                .cookie_same_site(SameSite::None)
                .build(),
            )
...

To recap, all cookies now have Secure set to true, and SameSite set to None. 💥 While this currently satisfies Chromium browsers, it comes with a new warning. There’s no assurance that these cookies will continue to be accepted in the future, as illustrated by the Chrome example below.

Firefox does not show any cookie warnings, we will not show any screenshots.

Opera accepts the server-side per-request cookies. Log in using an invalid email, we get the expected response:


Chrome also accepts the cookies, but shows a new warning:


The warning is:

Setting cookie in cross-site context will be blocked in future Chrome versions

Cookies with the SameSite=None; Secure and not Partitioned attributes that operate in cross-site contexts are third-party cookies. In future Chrome versions, setting third-party cookies will be blocked. This behavior protects user data from cross-site tracking.

Please refer to the article linked to learn more about preparing your site to avoid potential breakage.

We will briefly discuss this warning in the next section.

Chromium is in the process of phasing out of third-party cookies!

The article linked Chrome mentions in the warning is:

🚀 Prepare for third-party cookie restrictions

I have not read everything yet, but it does look very comprehensive, listing a lot of alternatives to third-party cookies.

I can’t remember how, but I came across this Mdm Web Docs article titled Cookies Having Independent Partitioned State (CHIPS)
before encountering the Chrome article mentioned above. It explains Partitioned cookie. Subsequently, I reached out to the authors of the relevant crates to inquire about this topic:

It appears that they are going to support this Partitioned cookie. We’ll just have to wait and see how it pans out.

I haven’t delved into cookies for a while, and there have been changes. I feel up-to-date with cookies now! 😂 It has been an interesting issue to study. I hope you find the information in this post useful. Thank you for reading. And stay safe, as always.

✿✿✿

Feature image source:

🦀 Index of the Complete Series.