Problem: InvalidAuthenticityToken

Witam! Mam problem z resetowaniem haseł. Korzystałem z tutoriala znalezionego tutaj: https://github.com/rejeep/authlogic-password-reset-tutorial
Logowanie działa, aktywacja też działa natomiast w sytuacji gdy przyjdzie mail z linkiem do resetu, po wpisaniu hasła otrzymuje błąd:

ActionController::InvalidAuthenticityToken in Password resetsController#update

Password resets controller:

[code]class PasswordResetsController < ApplicationController
before_filter :require_no_user
before_filter :load_user_using_perishable_token, :only => [ :edit, :update ]

def new
@user_session = UserSession.new
end

def create
@user_session = UserSession.new
@user = User.find_by_email(params[:email])
if @user
@user.deliver_password_reset_instructions!
flash[:notice] = “E-mail z instrukcją resetu hasła został wysłany”
redirect_to root_path
else
flash.now[:error] = “Użytkownik z podanym e-mailem #{params[:email]} nie istnieje.”
redirect_to new_password_reset_url
end
end

def edit
@user_session = UserSession.new
end

def update
@user.password = params[:password]
#@user.password_confirmation = params[:password]
if @user.save
flash[:success] = “Hasło zostało zmienione”
UserSession.create(@user, false) # Log user in manually
redirect_to profil_url
else
render :action => :edit
end
end

private

def load_user_using_perishable_token
@user = User.find_using_perishable_token(params[:id])
unless @user
flash[:error] = “Konto nie zostało znalezione.”
redirect_to root_url
end
end
end[/code]
Kontroler userów wygląda następująco:

[code]class UsersController < ApplicationController
def new
@user_session = UserSession.new
@user = User.new
end

def create
@user = User.new(params[:user])

if @user.save_without_session_maintenance
  @user.deliver_activation_instructions!
  flash[:notice] = "Konto zostało utworzone. Sprawdź e-mail aby aktywować konto."
  redirect_to root_url
else
  render :action => 'new'
end

end

def edit
if current_user
@user = current_user
else
flash[:notice] = “Musisz być zalogowany…”
redirect_to root_url
end
end

def update
@user = User.find(params[:id])
if @user.update_attributes(params[:user])
flash[:notice] = “Edycja profilu zakończona powodzeniem.”
redirect_to root_url
else
render :action => ‘edit’
end
end

def show
@user = User.find_by_login(params[:id])
if @user.nil?
flash[:notice] = “Nie ma takiego użytkownika.”
redirect_to root_url
end
end

end[/code]

skip_before_filter :verify_authenticity_token, :only => [:edit]

A czy masz dodany w layoucie, w którym podajesz hasło csrf_meta_tag ?
Wg mnie właśnie brak tego tagu może być problemem, a wyłączanie weryfikacji tego tokenu jest mało roztropne.
Mam tak samo zaimplementowany w jednej aplikacji mechanizm przywracania hasła i nie muszę się uciekać do sposobów typu:

skip_before_filter :verify_authenticity_token, :only => [:edit]

Jeśli masz do wykonania jakąś akcję do której link prowadzi z zewnętrznego serwisu (emaila) to podpada to pod atak CSRF.

Ten skip jest potrzebny ponieważ możliwość wykonania akcji rozpoznajemy po tokenie do resetowania hasła które jest w linku z emaila i możemy tutaj pominąć weryfikację tokenu zapobiegającego atakom CSRF.

@paneq - to było tak od zawsze, czy od RoR > 2.3.8?

Wpisanie : skip_before_filter :verify_authenticity_token, :only => [:edit]
Zakończyło się niepowodzeniem i pojawieniem się tego samego błędu. Domyśliłem się, że chodzić może nie o metodę edit, ale o update. Taka zmiana zaowocowała tym, że już nie wyświetla mi się błąd ze złym tokenem. Najgorsze jest jednak to, że hasło się nie zmienia i nie ma żadnego błędu.

Obecnie mój kontroler wygląda tak:

[code]class PasswordResetsController < ApplicationController
before_filter :require_no_user
before_filter :load_user_using_perishable_token, :only => [ :edit, :update ]
skip_before_filter :verify_authenticity_token, :only => [:update]

def new
@user_session = UserSession.new
end

def create
@user_session = UserSession.new
@user = User.find_by_email(params[:email])
if @user
@user.deliver_password_reset_instructions!
flash[:notice] = “E-mail z instrukcją resetu hasła został wysłany”
redirect_to root_path
else
flash.now[:error] = “Użytkownik z podanym e-mailem #{params[:email]} nie istnieje.”
redirect_to new_password_reset_url
end
end

def edit
@user_session = UserSession.new
end

def update
@user.password = params[:password]
#@user.password_confirmation = params[:password]
if @user.save
flash[:success] = “Hasło zostało zmienione”
UserSession.create(@user, false) # Log user in manually
redirect_to profil_url
else
render :action => :edit
end
end

private

def load_user_using_perishable_token
@user = User.find_using_perishable_token(params[:id])
unless @user
flash[:error] = “Konto nie zostało znalezione.”
redirect_to root_url
end
end
end[/code]
W chwili zmiany hasła po prostu jeszcze raz wyświetla się to samo (niepowodzenie), a hasło jest takie jakie było. Może coś nie tak z widokiem do edycji jest?


@EDIT: OK, teraz wszystko działa. Problem tkwił w tym, że nie miałem pola :password_confirmation . Mógłby mi ktoś powiedzieć skąd rails jest takie mądre? A w zasadzie gem authlogic? Wiąże się to z tym, że przy rejestracji tez to pole jest zawarte?

Generalnie to http://api.rubyonrails.org/classes/ActiveModel/Validations/HelperMethods.html#method-i-validates_confirmation_of :wink:

http://ar.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#M000081

a także: