Serving JS from controller action

Whenever I serve “static” javascript from a controller action, I get this error:

** (Plug.CSRFProtection.InvalidCrossOriginRequestError) security warning: an embedded <script> tag on another site requested protected JavaScript (if you know what you're doing, disable forgery protection for this route).

The code in the controller is this:

  def app_js(conn, _params) do
    conn
    |> put_resp_content_type("text/javascript")
    # @app_js is some JS read from a file
    |> send_resp(200, @app_js)
  end

The code in the template is this:

<script defer type="text/javascript" src="<%= Routes.static_assets_path(@conn, :app_js) %>"></script>

When I serve the same JS from the “static” pipeline everything works… What exactly is the difference?

Pipeline used by the static_assets route have plug :protect_from_forgery. You can create a new pipeline in your router that doesn’t include this plug and use it for the static_assets route.

Yes, I know I can disable forgery protection, but 1)I have some reasons for wanting to reuse the same pipeline and 2) I don’t understand why files served using the attic pipeline work and files served from the controller don’t work

For the first, pipelines are (kind of) composable, you can split one big into a few smaller and use one or all in different places.
For the second, there’s no “static pipeline”, but Plug.Static configured in Endpoint.ex, take a look at it, there’s quite a lot

When I said “static pipeline” I meant “pipeline with the Plug.Static plug”. I’m aware of the fact that I can compose pipeline, and as I said above I really want to be able to reuse the old pipeline as it is.

My main problem is that I don’t understand why files served from the controller trigger the invalid cross-origin request because I don’t think there’s any JavaScript making the request: the request is made from the scrip the in the webpage served by that same domain.

1 Like