lookbook-hq / lookbook

A UI development environment for Ruby on Rails apps ✨
https://lookbook.build
MIT License
894 stars 92 forks source link

ActionController::InvalidAuthenticityToken #632

Open lazaronixon opened 3 months ago

lazaronixon commented 3 months ago

Describe the bug

When I try to submit to a main application controller, I receive ActionController::InvalidAuthenticityToken. For some reason, the session cookie is not created when I access /lookbook, but as soon as I open the main application and the session cookie is set, the following requests work without any problem.

To Reproduce

Rails.application.routes.draw do
  mount Lookbook::Engine, at: "/lookbook"
  resource :slow_action, only: :create
end
class SlowActionsController < ApplicationController
  # skip_forgery_protection

  def create
    sleep 3.seconds; head :created
  end
end

test/components/previews/button_preview/loading.html.erb

<%= form_with(url: main_app.slow_action_path) do |form| %>
  <%= form.button tag.span("Click and wait"), class: "btn btn--loading" %>
<% end %>
allmarkedup commented 1 week ago

Hey @lazaronixon, sorry for the super slow reply and thanks for the bug report.

I've just opened a PR that should fix this. I realise it's quite a while since you created this issue but if you are able to to test it and confirm it works for you too then that would be fantastic:

gem "lookbook", github: "lookbook-hq/lookbook", branch: "fix-preview-csrf-exception"

From my testing it seems to fix the issue so if I don't hear back with any problems I'll merge it in and get it out in the next release. Thanks again for bringing it to my attention :)

halo commented 1 week ago

Thank you for attempting to fix this issue. I'm a bit confused, though.

This is what my lookbook HTML looks like with a form_for tag:

<!-- http://127.0.0.1:3005/lookbook/inspect/toggle/async -->
<html>

<head>
  <!-- Your PR adds this one -->
  <meta name="csrf-token" content="3JWhDXzu1713DX..." /> 
</head>

<body>
  <!-- I don't think I can programmatically escape this iframe, right? I can't see the token above. -->
  <iframe>
    <html>

    <head>
      <title>Preview</title>
      <!-- This is from my Rails layout -->
      <meta name="csrf-token" content="iAhD6iIsCjEhorYn...">  
    </head>

    <body>
      <form id="..." action="..." accept-charset="UTF-8" method="post">
        <!-- This is what `form_for` adds -->
        <input type="hidden" name="authenticity_token" value="ReJTSC8SlGyDbfWo..." autocomplete="off">
      </form>
    </body>

    </html>
  </iframe>
</body>

</html>

What's being submitted is the token ReJTSC8SlGyDbfWo.... And because my session is not initialized yet, it will lead to a ActionController::InvalidAuthenticityToken.

However, if I add session[:test] = 'something' to the controller that takes the request (and reload the page), I have a session that matches the access token being submitted and I don't get the exception.

Your PR does not change this behavior.

My guess is that the underlying problem is the <iframe> that prevents the session from being set automatically. I don't think this is related to the meta tag in the lookbook layout at all.

See also https://discuss.rubyonrails.org/t/cant-verify-csrf-token-authenticity-in-iframe/85518 (but that is only addressing cross-domain issues I think)

See also https://security.stackexchange.com/questions/238443/iframe-friendly-csrf-protection