Rust: actix-web CORS, Cookies and AJAX calls.
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.
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:
- Rust web application: MySQL server, sqlx, actix-web and tera.
- Rust: learning actix-web middleware 01.
- Rust: retrofit integration tests to an existing actix-web application.
- Rust: adding actix-session and actix-identity to an existing actix-web application.
- Rust: actix-web endpoints which accept both
application/x-www-form-urlencoded
andapplication/json
content types. - Rust: simple actix-web email-password login and request authentication using middleware.
- 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.
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
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:
-
AJAX requests must have both
xhrFields.withCredentials
andcrossDomain
set totrue
. - How session cookies are created. We will discuss these in detail in the following sections.
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:
-
Scheme
:HTTPS://
-
Secure
:true
-
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:
- Does actix-web support cookie "Partitioned" yet, please? #3284
- Does cookie-rs support cookie "Partitioned" yet, please? #224
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:
- https://www.omgubuntu.co.uk/2022/09/ubuntu-2210-kinetic-kudu-default-wallpaper
- https://in.pinterest.com/pin/337277459600111737/
- https://www.rust-lang.org/
- https://www.pngitem.com/download/ibmJoR_rust-language-hd-png-download/