16

I'm using play 2.4 to make a public REST API. I added the CORS filter allowing all origins and headers.

See from application.conf:

play.filters {
  # CORS filter configuration
  cors {

    # The path prefixes to filter.
    pathPrefixes = ["/"]

    # The allowed origins. If null, all origins are allowed.
    allowedOrigins = null

    # The allowed HTTP methods. If null, all methods are allowed
    allowedHttpMethods = null

    # The allowed HTTP headers. If null, all headers are allowed.
    allowedHttpHeaders = null

    # The exposed headers
    exposedHeaders = []

    # Whether to support credentials
    supportsCredentials = true

    # The maximum amount of time the CORS meta data should be cached by the client
    preflightMaxAge = 1 hour
  }
}

It works perfectly fine when I call the API from a classic browser (chrome/firefox tested), regardless of the origin as I allowed all.

BUT when I try to call it form a cordova application (in cordova apps, the origin of ajax requests are file://), I get a CORS error: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'file://' is therefore not allowed access. The response had HTTP status code 403. As if I didn't allow origin 'file://'

I tried to consume another API that allows CORS (GET https://public.opencpu.org/ocpu/library/) to check if it wasn't cordova that blocked the request but it worked fine. So I guess the problem comes from Play.

I tried to set allowedOrigins = ["file://"] but it doesn't work either...

Any help please?

EDIT: This isn't a duplicate of Cross origin GET from local file:// : I can't install a webserver as this is a cordova application. The files are served from the phone/tablet file system to the WebView. This is a Play framework specific question, I used to have no problems with an older version. Maybe the default CorsFilter can be modified to allow origin file://

EDIT 2: After it's been requested, here's the (very simple) code I use for my custom scala filter.

// CORSFilter.scala
package filters
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import play.api.mvc._
import play.mvc.Http

/**
  * Allow CORS from anywhere, any method
  */
class CORSFilter extends EssentialFilter {
  def apply(nextFilter: EssentialAction) = new EssentialAction {
    def apply(requestHeader: RequestHeader) = {
        nextFilter(requestHeader)
          .map { result =>
            if (requestHeader.method.equals("OPTIONS")) {
              Results.Ok.withHeaders(
                Http.HeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN -> "*",
                Http.HeaderNames.ACCESS_CONTROL_ALLOW_HEADERS -> "X-Requested-With, Accept, Content-Type",
                Http.HeaderNames.ACCESS_CONTROL_ALLOW_METHODS -> "HEAD,GET,POST,PUT,PATCH,DELETE")
            } else {
              result.withHeaders(
                Http.HeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN -> "*",
                Http.HeaderNames.ACCESS_CONTROL_ALLOW_HEADERS -> "X-Requested-With, Accept, Content-Type",
                Http.HeaderNames.ACCESS_CONTROL_ALLOW_METHODS -> "HEAD,GET,POST,PUT,PATCH,DELETE",
                Http.HeaderNames.ACCESS_CONTROL_EXPOSE_HEADERS -> "X-Custom-Header-To-Expose")
            }
          }
    }
  }
}

Note that I only use this in development mode and it has some issues. For exemple if an exception is not caught at runtime, the response won't have the CORS headers because the filter is not applied. But if this is for a cordova application, it should work fine enough.

Community
  • 1
  • 1
SebT
  • 472
  • 1
  • 4
  • 14
  • What does your cordova implementation code look like? – Justin Sep 10 '15 at 18:26
  • 1
    Hi, this is a duplicate of: http://stackoverflow.com/questions/8192159/cross-origin-get-from-local-file – Saar Sep 10 '15 at 18:26
  • @Saar I flagged it for you :) – Keith M Sep 10 '15 at 18:27
  • Install a local web server....there are lots of ways to do it and it doesn't take long – charlietfl Sep 10 '15 at 18:46
  • This isn't a duplicate Saar, my question is play specific. I just want to authorize requests from origin file://, which used to work in older versions. I can't install a local server as this is a cordova application charlitfl, filed are served from the mobile file system to the WebView – SebT Sep 11 '15 at 08:22
  • Notice the "The response had HTTP status code 403" message. I am under the impression that Play explicitly returns a 403 error when a request comes from origin 'null' or 'file://' – SebT Sep 11 '15 at 12:35
  • @Saar its not duplicate, and I have same issue ended with see this question. – Al-Mothafar Apr 14 '16 at 12:37
  • Did you ever resolved this? Did you ever tried to remove origin header from cordova/webview XMLHttpRequest? – Michael Burger Aug 21 '17 at 13:24
  • @MichaelBurger there is no way to remove the header from the request sent from cordova. Just use the selected answer below. Your play server should allow CORS. Or you can use nginx or apache to change the origin, this seems to be the cleanest solution – SebT Aug 21 '17 at 18:56

1 Answers1

9

Ok, the problem comes from this line of code in the Play framework: https://github.com/playframework/playframework/blob/master/framework/src/play-filters-helpers/src/main/scala/play/filters/cors/AbstractCORSPolicy.scala#L322

As the URI is 'file://', the domain/host of the URI is null so new URI throws an error and then Play returns a 403 error. To fix this we need to patch the framework or try to change the origin to 'file://'.

I found a workaround for my use case: disable CORS on Play. Then when I use ajax from my cordova app, the origin isn't checked.

The problem with this workaround is that if I want to enable CORS on my Play project for any reason, the cordova app won't work since Play will check the origin and reject it.

SebT
  • 472
  • 1
  • 4
  • 14
  • Can you please explain how do you disable CORS? – Simon Apr 05 '16 at 15:34
  • 1
    Well I had enabled the play filter [CorsFilter](https://www.playframework.com/documentation/2.4.x/CorsFilter). When I didn't need it for my cordova app to work, I just removed it. The problem then was that if I tested my cordova app on the browser, I had CORS errors if I didn't disabled the cross domain security in chrome. So later I just made my own CORSFilter that add needed headers for every request without any check. – SebT Apr 13 '16 at 12:57
  • @SebT I'm facing the same situation. Could you share your code for the CORSFilter that you made? – Slartibartfast Nov 30 '16 at 10:12