Turbo stream and authenticity token

After updating a record, I use a turbo stream to send out some new command buttons to all users involved in that topic. It is a button_to in this case.

When I click on the button, I get the error ActionController::InvalidAuthenticityToken and get logged out. Everything works fine, if I load the modal by clicking on the record in the list items.

If anyone has any ideas about what could be going wrong, I am all ears.

Related to:

  1. Turbo stream partial with button_to tag throws ActionView::Template::Error · Issue #243 · hotwired/turbo-rails · GitHub
  2. Stop failing GSRF token generation when session is disabled by casperisfine · Pull Request #43427 · rails/rails · GitHub
  3. button_to: Support `authenticity_token:` option by seanpdoyle · Pull Request #43417 · rails/rails · GitHub

button_to rails helper creates a form, which includes a unique authenticity token (hidden form field) linked to the users session. It’s likely the created button_to contains a hidden field with an authenticity token not valid for the current session you are testing.

@tleish the problem is that there is no authenticity token generated. Not even adding authenticity_token: true provides that for me.

I know what the problem is, I am interested in solutions. Currently trying out mrujs

This solutions works for me:

# config/importmap.rb

pin "mrujs", to: "https://ga.jspm.io/npm:mrujs@0.7.1/dist/mrujs.module.js"
pin "morphdom", to: "https://ga.jspm.io/npm:morphdom@2.6.1/dist/morphdom.js"
// app/javascript/application.js

import "@hotwired/turbo-rails"
import "controllers"
import mrujs from "mrujs"
mrujs.start()
// app/javascript/controllers/authentic_controller.js

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  connect() {
    if (this.element.querySelector("input[name='authenticity_token']") == null) {
      this.element.closest("form").appendChild(this.authenticityToken)
    }
  }

  get authenticityToken() {
    const input = document.createElement("input")

    input.type = "hidden"
    input.name = "authenticity_token"
    input.autocomplete = "off"
    input.value = window.mrujs.csrfToken()

    return input
  }
}

Then in the HTML:

  <%= button_to I18n.t("model.buttons.collect_payment"),
                          method: :post,
                          class: "btn btn-green",
                          data: {
                            controller: "authentic",
                            action: "click->toggle#hide",
                          } %>

Now the authenticity token is added properly