Skip to content

CORS settings in default CORS middleware #294

@zekageri

Description

@zekageri

Platform

ESP32

IDE / Tooling

pioarduino

What happened?

When testing, I often run my react app in dev mode on localhost.
The frontend app api points to my esp32 ip address in this case, which is in the local network.
The following ( recommended ) cors config in this case is the following

corsMiddleware.setHeaders("Origin, X-Requested-With, Content-Type, Accept, Authorization, SysAuthorization");
corsMiddleware.setMethods("GET,HEAD,OPTIONS,POST,PUT,DELETE");
corsMiddleware.setOrigin("*");

When _credentials is true, the browser does not let "*" wildcard origin and it rejects the OPTIONS request. This is expected browser behaviour.
In order for this to work properly, we must set the origin to the request origin. This can not be done globally, but on individual requests.
I have cloned the AsyncCorsMiddleware and added a void checkAllowAnyOrigin(AsyncWebServerRequest *request, AsyncWebServerResponse *response); method. This method sets the Origin header to the request Origin, passes the browser check.

void ClonedCorsMiddleware::checkAllowAnyOrigin(AsyncWebServerRequest *request, AsyncWebServerResponse *response) {
    if (allowAnyOrigin) {
        std::string incomingOrigin = request->getHeader(asyncsrv::T_CORS_O)->value().c_str();
        response->addHeader(asyncsrv::T_CORS_ACAO, incomingOrigin.c_str());
    }
}

void ClonedCorsMiddleware::run(AsyncWebServerRequest *request, ArMiddlewareNext next) {
    // Origin header ? => CORS handling
    if (request->hasHeader(asyncsrv::T_CORS_O)) {
        // check if this is a preflight request => handle it and return
        if (request->method() == HTTP_OPTIONS) {
            AsyncWebServerResponse *response = request->beginResponse(200);
            addCORSHeaders(response);
            checkAllowAnyOrigin(request, response);
            request->send(response);
            return;
        }

        // CORS request, no options => let the request pass and add CORS headers after
        next();
        AsyncWebServerResponse *response = request->getResponse();
        if (response) {
            addCORSHeaders(response);
            checkAllowAnyOrigin(request, response);
        }

    } else {
        // NO Origin header => no CORS handling
        next();
    }
}

This is a hack right now because the _origin is set twice in the run method. I suggest a separate bool variable which can be toggled ( like allowAnyOrigin or allowCurrentOrigin ) which would allow the current Origin in the request.

Stack Trace

The code does not crash but browser does not care about the wildcard Origin header when credentials are true.

Minimal Reproductible Example (MRE)

Use the default CORS middleware with _credentials = true and try to reach the esp32 from an other ip address using origin as "*"

I confirm that:

  • I have read the documentation.
  • I have searched for similar discussions.
  • I have searched for similar issues.
  • I have looked at the examples.
  • I have upgraded to the lasted version of ESPAsyncWebServer (and AsyncTCP for ESP32).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type: BugSomething isn't working

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions