Не удается проверить подлинность токена CSRF с помощью пользовательских требований к паролю

У меня есть API Rails 5, и я настраиваю аутентификацию. Я добавил некоторые пользовательские требования к паролю, и все работает для создания учетной записи и выхода из учетной записи, но я получаю ошибку Completed 422 Unprocessable Entity, которая сопровождается сообщением Can't verify CSRF token authenticity всякий раз, когда я пытаюсь войти в систему. Когда я удаляю строку, устанавливающую пользовательский проверка пароля, все работает.

Я добавил protect_from_forgery with: :null_session в свой контроллер сеансов, но это не повлияло.

Модель регистрации:

class Registration < ApplicationRecord
  @username_length = (3..20)

  PASSWORD_CONFIRMATION = /\A
    (?=.{8,})          # Must contain 8 or more characters
    (?=.*\d)           # Must contain a digit
    (?=.*[a-z])        # Must contain a lower case character
    (?=.*[A-Z])        # Must contain an upper case character
    (?=.*[[:^alnum:]]) # Must contain a symbol
  /x

  validates :username, uniqueness: true, length: @username_length
  has_secure_password
  validates :password, format: PASSWORD_CONFIRMATION
  has_secure_token :auth_token

  #used to logout
  def invalidate_token
    self.update_columns(auth_token: nil)
  end

  # makes sure use of built-in auth method bcrypt gives and hashes the password
  # against the password_digest in the db
  def self.validate_login(username, password)
    registration = find_by(username: username)
    if registration && registration.authenticate(password)
      registration
    end
  end
end

Контроллер сеансов

class SessionsController < ApiController
  skip_before_action :require_login, only: [:create], raise: false
  protect_from_forgery with: :null_session

  def create
    if registration = Registration.validate_login(params[:username], params[:password])
      allow_token_to_be_used_only_once_for(registration)
      send_token_for_valid_login_of(registration)
    else
      render_unauthorized('Error with your login or password')
    end
  end

  def destroy
    logout
    head :ok
  end

  private

  def send_token_for_valid_login_of(registration)
    render json: { token: registration.auth_token }
  end

  def allow_token_to_be_used_only_once_for(registration)
    registration.regenerate_auth_token
  end

  def logout
    current_registration.invalidate_token
  end
end

Регистрационный контроллер

class RegistrationController < ApplicationController
  skip_before_action :verify_authenticity_token

  def index; end

  def custom
    user = Registration.create!(registration_params)
    puts "NEW USER #{user}"
    render json: { token: user.auth_token, id: user.id }
  end

  def profile
    user = Registration.find_by_auth_token!(request.headers[:token])
    render json: {
      user: { username: user.username, email: user.email, name: user.name }
    }
  end

  private

  def registration_params
    params.require(:registration).permit(:username, :email, :password, :name)
  end
end

В идеале при входе в систему должно возвращаться сообщение 200 с токеном аутентификации, как и при создании учетной записи.


person Daniel    schedule 08.10.2019    source источник
comment
Я еще немного покопался в has_secure_password и попытался отключить проверки по умолчанию, отправив validations: false, но это не повлияло на ошибку.   -  person Daniel    schedule 09.10.2019


Ответы (2)


Попробуйте добавить проверку в виде хэша с ключом :with

validates :password, format: {with: PASSWORD_CONFIRMATION}

https://guides.rubyonrails.org/active_record_validations.html#format

person arieljuod    schedule 09.10.2019
comment
Я пытался внести это изменение, но это не повлияло на возвращаемую ошибку. Я также попытался переместить проверку в этот хэш вместо того, чтобы устанавливать его как переменную, но это также не повлияло. - person Daniel; 09.10.2019

Было две вещи, которые я должен был настроить. Во-первых, мне нужно было передать флаг validations false в моей модели регистрации, поэтому has_secure_password стало has_secure_password :validations => false.

Во-вторых, я называл неправильное имя поля password в модели. Вызвав переменную PASSWORD_CONFIRMATION на password_digest вместо password, мой логин начал работать.

person Daniel    schedule 11.10.2019