Search code examples
javascripthtmlrustcorsrust-axum

CORS error on client communicating with Web API


I programmed a Web-API using rust-axum and I made a client/frontend using just html and javascript Request. When I try to make a request from my client (opening the html file or serving a server e.g. via python -m http.serve) and making a request I got the following error:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://127.0.0.1:3000/v1/auth/sign_in. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 405.

When I add .layer(CorsLayer::permissive()) to my server I can make the requests without any CORS issues, but I can't set cookies anymore, which results in the users not beeing able to log in. When I add .layer(CorsLayer::permissive().allow_credentials(true)) so I can set cookies again, I get the runtime error:

thread 'main' panicked at 'Invalid CORS configuration: Cannot combine `Access-Control-Allow-Credentials: true` with `Access-Control-Allow-Headers: *`', /home/mobergmann/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tower-http-0.4.4/src/cors/mod.rs:714:9

How can I change my configuration (client and/or server) so, that I can make Requests, not resulting in a CORS conflict, and be able to set cookies? The API should be public, meaning that any other user should be able to write their own client or hosts my client.


Solution

  • The API should be public, meaning, that ay other user should be able to write their own client or hosts my client.

    This is extremely dangerous when combined with credentials the browser sends automatically (like cookies).

    Consider a user who logs into your API using a client that they trust. Then they visit Mallory's Malicious Website which makes an Ajax request to your API. The user already has credentials in their browser and your API allows Mallory's Malicious Website access. You've given the attacker permission to help themselves to any data and send any instructions they like.

    You should really consider using a different approach (such as an Authorization request header with a value that includes a token fetched via OAuth).


    That said, see the error message:

    Cannot combine Access-Control-Allow-Credentials: true with Access-Control-Allow-Headers: *

    So you need to not use Access-Control-Allow-Headers: *.

    Clearly that is what permissive() does, so that needs replacing with something else which sends the specific headers you want to allow in the response instead of the wildcard.

    Now, I've not used Rust to do this, so let's take a look at the documentation.

    use tower_http::cors::CorsLayer;
    use http::header::{AUTHORIZATION, ACCEPT};
    
    let layer = CorsLayer::new().allow_headers([AUTHORIZATION, ACCEPT]);
    

    Obviously you'll need to update that for the headers you are actually using, and combine it with your existing rules.

    Presumably with:

    .layer(CorsLayer::new().allow_origin(AllowOrigin::predicate(
        |origin: &HeaderValue, _request_parts: &RequestParts| {
            // origin.as_bytes().ends_with(b".rust-lang.org")
            true
        },
    )).allow_headers([AUTHORIZATION, ACCEPT]).allow_credentials(true))