Skip to content

CSRF conflicts when embedding in Phoenix applications cause assets to fail loading #52

@camilohollanda

Description

@camilohollanda

Problem Description

When embedding FunWithFlags UI in Phoenix applications (or other frameworks with built-in CSRF protection), static assets (JavaScript/CSS) fail to load due to CSRF conflicts.

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)

Root Cause

  1. FunWithFlags UI serves static assets via Plug.Static at /assets/*
  2. All requests (including assets) go through FunWithFlags' CSRF protection pipeline
  3. When embedded in Phoenix apps, both Phoenix and FunWithFlags try to manage CSRF protection
  4. Phoenix treats FunWithFlags asset requests as potentially dangerous cross-origin requests
  5. JavaScript and CSS assets are blocked, breaking the UI

Current README Solution vs. User Experience Reality

README Documents a Workaround

The README mentions using a special :mounted_apps pipeline:

pipeline :mounted_apps do
  plug :accepts, ["html"]
  plug :put_secure_browser_headers
  # Notice: NO :protect_from_forgery
end

scope path: "/feature-flags" do
  pipe_through :mounted_apps  # Use special pipeline
  forward "/", FunWithFlags.UI.Router
end

Note: There is no need to add :protect_from_forgery to the :mounted_apps pipeline because this package already implements CSRF protection.

UX Problems with README Approach

1. Discovery Problem

  • Users naturally try the :browser pipeline first (standard Phoenix practice)
  • Hit the CSRF error with no clear guidance toward the solution
  • Must dig through README to find the :mounted_apps workaround

2. Not Intuitive
Most Phoenix developers expect this to work:

scope "/admin" do
  pipe_through [:browser, :require_authenticated_user]  # Natural approach
  forward "/feature-flags", FunWithFlags.UI.Router
end

3. Documentation Gap

  • README mentions :mounted_apps but doesn't clearly explain WHY you need it
  • No clear guidance on when you need this special pipeline vs. regular :browser
  • Users must understand CSRF conflicts to know they need the workaround

Reproduction Steps

  1. Create a Phoenix application with CSRF protection (default setup)
  2. Add FunWithFlags UI dependency
  3. Try the obvious/intuitive approach - mount using :browser pipeline:
    scope "/admin" do
      pipe_through [:browser, :require_authenticated_user, :require_admin]
      forward "/feature-flags", FunWithFlags.UI.Router
    end
  4. Navigate to /admin/feature-flags
  5. Browser console shows CSRF errors and UI assets fail to load
  6. User gets stuck and has to research/discover the :mounted_apps workaround

Real-World Evidence

Despite the README documentation, users consistently create their own workarounds:

# Example from actual Phoenix apps
pipeline :no_csrf do
  plug :accepts, ["html"]
  plug :fetch_session
  # Skip :protect_from_forgery - FunWithFlags has built-in CSRF protection
end

This shows the README solution has poor discoverability and user experience.

Expected Behavior

FunWithFlags UI should work seamlessly when embedded in Phoenix applications using the standard :browser pipeline without requiring:

  • Special documentation discovery
  • Custom pipeline creation
  • Understanding of CSRF conflict internals

Solution

Implement automatic pipeline separation within FunWithFlags UI to handle CSRF conflicts transparently - see PR #51 for the fix that makes the obvious approach "just work".

Environment

  • Phoenix applications with default CSRF protection
  • FunWithFlags UI embedded via forward in router using standard :browser pipeline
  • Any Elixir web framework with CSRF protection that conflicts with Plug.CSRFProtection

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions