-
Notifications
You must be signed in to change notification settings - Fork 37
Description
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
- FunWithFlags UI serves static assets via
Plug.Staticat/assets/* - All requests (including assets) go through FunWithFlags' CSRF protection pipeline
- When embedded in Phoenix apps, both Phoenix and FunWithFlags try to manage CSRF protection
- Phoenix treats FunWithFlags asset requests as potentially dangerous cross-origin requests
- 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
endNote: 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
:browserpipeline first (standard Phoenix practice) - Hit the CSRF error with no clear guidance toward the solution
- Must dig through README to find the
:mounted_appsworkaround
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
end3. Documentation Gap
- README mentions
:mounted_appsbut 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
- Create a Phoenix application with CSRF protection (default setup)
- Add FunWithFlags UI dependency
- Try the obvious/intuitive approach - mount using
:browserpipeline:scope "/admin" do pipe_through [:browser, :require_authenticated_user, :require_admin] forward "/feature-flags", FunWithFlags.UI.Router end
- Navigate to
/admin/feature-flags - Browser console shows CSRF errors and UI assets fail to load
- User gets stuck and has to research/discover the
:mounted_appsworkaround
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
endThis 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
forwardin router using standard:browserpipeline - Any Elixir web framework with CSRF protection that conflicts with Plug.CSRFProtection