Errore "ActionController :: InvalidAuthenticityToken" quando si utilizza form_with
Ho un modulo simile a questo:
<%= 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 %>
Subito dopo l'invio del modulo:
Appunti:
- Ciò si verifica in un'app (non in un'API), quindi le sessioni sono importanti, quindi la protezione CSRF deve essere lasciata attiva.
- Il problema si verifica in Chrome, in incognito e in Safari.
- Ho provato ad accedere con diversi utenti e a cancellare i cookie (nel caso fosse causato da un token non aggiornato )
Qualche altro messaggio di errore:
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'
Aggiornare
Sono tornato all'ultima versione funzionante del modulo, che era esattamente la stessa di sopra ma senza , local: true. Poi improvvisamente funziona! (nessun errore).
Ho pensato local: true(o remote: false) semplicemente disattiva l'invio del modulo ajax. Quindi non capisco perché ciò farebbe differenza (o avrebbe qualcosa a che fare con CSRF), sembra che questi due aspetti non siano correlati e non è chiaro il motivo per cui questi due concetti avrebbero qualche effetto l'uno sull'altro
Aggiorna 2
In seguito mi sono reso conto che anche un'altra forma precedentemente funzionante intatta produceva questo errore. Non era stato modificato in alcun modo. L'ho provato in Chrome in incognito e ha prodotto l'errore. Mezz'ora dopo (senza modificare alcun codice) l'ho provato di nuovo nello stesso browser e ha funzionato. Questo comportamento (molto) strano mi fa pensare che abbia a che fare con sessioni, cookie o cache. Riferirò se avrò ulteriori informazioni
Aggiorna 3
Dopo aver letto la soluzione di Sarah aggiunta protect_from_forgery prepend: trueal controller dell'applicazione (ho provato sia prima che dopo before_action :authenticate_user!), lo stesso messaggio di errore appare nei log, la richiesta POST non viene eseguita, ma l'app reindirizza alla home page. Cioè al POST vedo:
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)
Aggiorna 4
Ho tentato di cancellare manualmente la cache dei frammenti di rails (con Rails.cache.clear ). Ma il risultato è esattamente lo stesso prima / dopo aver cancellato la cache dei frammenti.
Risposte
Ricordo di essermi imbattuto in qualcosa di simile e di averlo risolto protect_from_forgery prepend: trueprima di qualsiasi autenticazione utente ApplicationController.
TL; DR: dopo 2 settimane di tentativi di debug, ho spento i turbolink e il problema è andato via .
Oltre a disattivare i turbolink, sembra che un'altra soluzione sia (menzionata qui ) aggiungendola aapplication.js
$(document).on('turbolinks:load', function(){ $.rails.refreshCSRFTokens(); });
Risposta precedente
Il problema continuava a riemergere. Ho provato le seguenti sei cose ma non l'ho ancora risolto
1. Cancella la cache dei frammenti
Rails.cache.clear( attenzione perché cancella la cache, rimuoverà anche cose come sidekiq jobs ecc.). Questo rimuoverà il token non aggiornato e l'aggiornamento dell'app nel browser riporterà le cose alla normalità e il modulo dovrebbe essere inviato (un semplice 'reinvio' non funzionerà, quindi torna alla pagina del modulo, aggiorna, quindi invia e dovrebbe lavoro)
2. Pagina di aggiornamento hard
Premi cmd+ opt+ jper aprire la console per sviluppatori, quindi fai clic con il pulsante destro del mouse su Aggiorna e seleziona "Svuota cache e ricarica hardware"
3. Elimina i cookie del sito
Fare clic con il tasto destro sulla piccola icona immediatamente a sinistra dell'URL (sarà un lucchetto se si utilizza https o la lettera "i" se si utilizza http). Entra in ciascuna delle categorie elencate (es. "Cookie", "Impostazioni sito", ecc.) E cancellale tutte
4. Eliminare i cookie per altri URL che puntano allo stesso sito
Ad esempio, se il tuo sito è www.example.com, ed è ospitato su heroku in www.example.herokuapp.com, allora elimina i cookie anche per quel secondo URL
5. Eliminare i cookie per localhost
Ho cancellato i cookie localhost solo per essere sicuro
6. Test in istanze completamente isolate di chrome