Erro “ActionController :: InvalidAuthenticityToken” ao usar form_with
Eu tenho um formulário parecido com este:
<%= form_with(url: star.starname, method: :post, local: true) do |f| %>
<% star.availabilities.each do |avail| %>
<%= f.label avail.time_slot %>
<%= radio_button_tag(:time_slot, avail.time_slot) %> <br>
<% end %>
<%= f.submit "Create" %>
<% end %>
Imediatamente após o envio do formulário:
Notas:
- Isso está ocorrendo em um aplicativo (não em uma API), portanto, as sessões são importantes, portanto, a proteção CSRF deve ser mantida ativada.
- O problema ocorre em cromo, incógnito e safari.
- Tentei fazer login com diferentes usuários e limpar os cookies (no caso de estar sendo causado por um token obsoleto )
Mais algumas mensagens de erro:
Started POST "/talljohn" for ::1 at 2020-09-16 10:06:21 +1000
Processing by StarsController#book as HTML
Parameters: {"authenticity_token"=>"P++4a+giwUBqZgCLfMwqKpMu0EGitd8zTOi5RWsnxpKlNcjiuU6hd3ebbIC/IOxlL74RJIvrq+yDuA1ZtfcvFw==", "time_slot"=>"2020-09-16 01:00:00 UTC", "commit"=>"Create", "starname"=>"talljohn"}
Can't verify CSRF token authenticity.
Completed 422 Unprocessable Entity in 1ms (ActiveRecord: 0.0ms | Allocations: 655)
ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):
actionpack (6.0.3.2) lib/action_controller/metal/request_forgery_protection.rb:215:in `handle_unverified_request'
actionpack (6.0.3.2) lib/action_controller/metal/request_forgery_protection.rb:247:in `handle_unverified_request'
devise (4.7.2) lib/devise/controllers/helpers.rb:255:in `handle_unverified_request'
actionpack (6.0.3.2) lib/action_controller/metal/request_forgery_protection.rb:242:in `verify_authenticity_token'
activesupport (6.0.3.2) lib/active_support/callbacks.rb:428:in `block in make_lambda'
activesupport (6.0.3.2) lib/active_support/callbacks.rb:200:in `block (2 levels) in halting'
actionpack (6.0.3.2) lib/abstract_controller/callbacks.rb:34:in `block (2 levels) in <module:Callbacks>'
activesupport (6.0.3.2) lib/active_support/callbacks.rb:201:in `block in halting'
Atualizar
Eu voltei para a última versão de trabalho do formulário, que era exatamente igual ao anterior, mas sem , local: true. Então de repente funciona! (Sem erros).
Eu pensei local: true(ou remote: false) simplesmente desativa o envio de formulário Ajax. Então, eu não entendo por que isso faria qualquer diferença (ou tenha algo a ver com CSRF), parece que esses dois aspectos não estão relacionados e não está claro por que esses dois conceitos teriam algum efeito um sobre o outro
Atualização 2
Mais tarde, percebi que outra forma de trabalho anterior intocada também produzia esse erro. Não havia sido alterado de forma alguma. Eu tentei no Chrome incógnito e produziu o erro. Meia hora depois (sem alterar nenhum código), tentei novamente no mesmo navegador e funcionou. Esse comportamento (muito) estranho me faz pensar que tem algo a ver com sessões, cookies ou cache. Vou relatar se eu aprender mais alguma coisa
Atualização 3
Depois de ler a solução de Sarah adicionando protect_from_forgery prepend: trueao controlador de aplicativo (tentei antes e depois before_action :authenticate_user!), a mesma mensagem de erro aparece nos logs, a solicitação POST não é acionada, mas o aplicativo redireciona para a página inicial. Ou seja, no POST, vejo:
Can't verify CSRF token authenticity.
Completed 401 Unauthorized in 1ms (ActiveRecord: 0.0ms | Allocations: 444)
Started GET "/users/sign_in" for ::1 at 2020-09-17 21:08:42 +1000
Processing by Devise::SessionsController#new as HTML
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
Redirected to http://localhost:3000/
Filter chain halted as :require_no_authentication rendered or redirected
Completed 302 Found in 3ms (ActiveRecord: 0.5ms | Allocations: 1900)
Atualização 4
Tentei limpar manualmente o cache de fragmento de trilhos (com Rails.cache.clear ). Mas o resultado é exatamente o mesmo antes / depois de limpar o cache do fragmento.
Respostas
Lembro-me de ter encontrado algo assim e adicionar protect_from_forgery prepend: trueantes de qualquer autenticação de usuário para ApplicationControllerresolvê-lo.
TL; DR: após 2 semanas de tentativas de depuração, desliguei os turbolinks e o problema foi embora .
Além de desligar os turbolinks, outra solução parece ser (mencionada aqui ) adicionando isso aoapplication.js
$(document).on('turbolinks:load', function(){ $.rails.refreshCSRFTokens(); });
Resposta anterior
O problema continuou ressurgindo. Eu tentei as seis coisas a seguir, mas ainda não resolvi
1. Limpe o cache do fragmento
Rails.cache.clear( aviso porque limpa o cache, ele também removerá coisas como sidekiq jobs etc). Isso removerá o token desatualizado e a atualização do aplicativo no navegador retornará as coisas ao normal, e o formulário deve ser enviado (um simples 'reenvio' não funcionará, então volte para a página do formulário, atualize e envie e deve trabalhos)
2. Página de atualização total
Pressione cmd+ opt+ jpara abrir o console do desenvolvedor, clique com o botão direito em atualizar e selecione 'Esvaziar Cache e Atualizar Hard
3. Exclua os cookies do site
Clique com o botão direito no ícone minúsculo imediatamente à esquerda do url (será um cadeado se estiver usando https ou a letra 'i' se estiver usando http). Vá para cada uma das categorias listadas (por exemplo, 'Cookies', 'Configurações do site' etc) e exclua todos eles
4. Exclua os cookies de outros urls que apontam para o mesmo site
Por exemplo, se o seu site é www.example.come está hospedado no heroku em www.example.herokuapp.com, exclua os cookies desse segundo URL também
5. Exclua cookies para localhost
Excluí cookies de host local apenas para ter certeza
6. Teste em instâncias completamente isoladas de cromo