micronaut-core icon indicating copy to clipboard operation
micronaut-core copied to clipboard

Micronaut Cors does not work as expected when we define custom HttpServerFilters

Open somitmittal opened this issue 1 year ago • 6 comments

Expected Behavior

OPTIONS preflight request should not fail

Actual Behaviour

OPTIONS preflight request fails

Steps To Reproduce

  1. Enable cors on micronaut application using micronaut.server.cors.enabled = true in application.yml
  2. Define another HttpServerFilter for e.g. AuthFilter that basically checks for "Authorization" header in a request and return s 401 if token is missing.
  3. On adding custom filter, when OPTIONS preflight request is made to the application, the custom filter executes and checks for "Authorization" header and since it is missing in OPTIONS preflight request, the custom filter returns a 401 failing the OPTIONS request resulting in a CORS error.
11:57:04.377 io.micronaut.runtime.Micronaut [?:?] [] - Startup completed in 9762ms. Server Running: http://localhost:8080
11:57:12.782 s.f.filters.AuthorizationFilter [?:?] [] - Invalid Token Exception for Url: /v2/api and Method: POST Exception: { error: UNAUTHORIZED, httpStatus: UNAUTHORIZED, httpStatusCode: 401 }
11:57:12.786 s.f.filters.AuthorizationFilter [?:?] [] - ErrorSummary: exception: { error: UNAUTHORIZED, httpStatus: UNAUTHORIZED, httpStatusCode: 401 }, uri: /v2/api, apiMethod: POST, apiVersion: v2, 

curl:

curl --location --request OPTIONS 'http://localhost:8080/v2/api' \
--header 'Origin: postman' \
--header 'Access-Control-Request-Method: POST'

Response: Screenshot 2024-05-31 at 12 12 09 PM

Environment Information

Micronaut: 3.7.2 Language: Kotlin

Example Application

No response

Version

3.7.2

somitmittal avatar May 31 '24 06:05 somitmittal

this sounds like you have the filter ordering incorrect

graemerocher avatar May 31 '24 06:05 graemerocher

@graemerocher thanks for quick reply. Is Cors filter of highest precedence?

As per cors filter code in Micronaut. I believe it is returning a response decorated with preflight headers if status is < 400. In this case, how can I specify cors filter to be of highest precedence?

@NonNull
    private Publisher<MutableHttpResponse<?>> handlePreflightRequest(@NonNull HttpRequest<?> request, @NonNull ServerFilterChain chain, @NonNull CorsOriginConfiguration corsOriginConfiguration) {
        Optional<HttpStatus> statusOptional = this.validatePreflightRequest(request, corsOriginConfiguration);
        if (statusOptional.isPresent()) {
            HttpStatus status = (HttpStatus)statusOptional.get();
            if (status.getCode() >= 400) {
                return Publishers.just(HttpResponse.status(status));
            } else {
                MutableHttpResponse<?> resp = HttpResponse.status(status);
                this.decorateResponseWithHeadersForPreflightRequest(request, resp, corsOriginConfiguration);
                this.decorateResponseWithHeaders(request, resp, corsOriginConfiguration);
                return Publishers.just(resp);
            }
        } else {
            return Publishers.then(chain.proceed(request), (respx) -> {
                this.decorateResponseWithHeadersForPreflightRequest(request, respx, corsOriginConfiguration);
                this.decorateResponseWithHeaders(request, respx, corsOriginConfiguration);
            });
        }
    }

Can cors filter ever proceed with chain request if options preflight request succeeded and land into custom authorization filter and still fail there?

somitmittal avatar May 31 '24 07:05 somitmittal

@graemerocher can you please suggest how can I fix the ordering so that cors filter runs first?

somitmittal avatar Jun 01 '24 06:06 somitmittal

Add an order to your own filter that is higher than that of the cors filter, which should be 9250

yawkat avatar Jun 01 '24 08:06 yawkat

@yawkat Few findings: On using @Order(Ordered.LOWEST_PRECEDENCE) on the custom filter, its not working. It fails on OPTIONS request.

curl --location --request OPTIONS 'http://localhost:8080/v2/api' \
--header 'Origin: http://www.google.com' \
--header 'Access-Control-Request-Method: POST'

But overriding the method getOrder works

override fun getOrder(): Int {
        return Integer.MAX_VALUE
    }

somitmittal avatar Jun 01 '24 10:06 somitmittal

Hey any updates here?

somitmittal avatar Jun 05 '24 09:06 somitmittal