micronaut-core
micronaut-core copied to clipboard
Micronaut Cors does not work as expected when we define custom HttpServerFilters
Expected Behavior
OPTIONS preflight request should not fail
Actual Behaviour
OPTIONS preflight request fails
Steps To Reproduce
- Enable cors on micronaut application using micronaut.server.cors.enabled = true in application.yml
- Define another HttpServerFilter for e.g. AuthFilter that basically checks for "Authorization" header in a request and return s 401 if token is missing.
- 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:
Environment Information
Micronaut: 3.7.2 Language: Kotlin
Example Application
No response
Version
3.7.2
this sounds like you have the filter ordering incorrect
@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?
@graemerocher can you please suggest how can I fix the ordering so that cors filter runs first?
Add an order to your own filter that is higher than that of the cors filter, which should be 9250
@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
}
Hey any updates here?