Robert
Robert

Reputation: 273

password param not being included in strong params

I'm using Bcrypt with has_secure_password in my User class, and trying to create a new User with username and password, but the password param, although permitted in my strong params, is not coming through.

This is my User class:

class User < ApplicationRecord
  has_secure_password

  has_many :journals
end

This is the User Controller:

class Api::V1::UsersController < ActionController::API
  def index
    @users = User.all
    render json: @users.to_json(:include => :journals)
  end

  def create
    byebug
    @user = User.create(user_params)
  end

  private

  def user_params
    params.require(:user).permit(:username, :password)
  end
end

I'm trying to post a new user with:

fetch('http://localhost:3001/api/v1/users', {
method: 'POST', 
headers: {Accept: 'application/json', "Content-Type": 
'application/json'}, 
body: JSON.stringify({username: 'Name', password: 'test'})})

and the result, inside the byebug in 'create', is:

(byebug) params
<ActionController::Parameters {"username"=>"Name", "password"=>"test", 
"controller"=>"api/v1/users", "action"=>"create", "user"=> . 
{"username"=>"Name"}} permitted: false>
(byebug) user_params
<ActionController::Parameters {"username"=>"Name"} permitted: true> 

No matter what I try, I cannot access the password param in user_params to create a user with a hashed password.

In terminal, in rails console, I can create a user and the password gets hashed as expected, I can then fetch that user information from the API with a normal fetch request, but I cannot use strong params to create a User with a Post request, and I don't know why.

Upvotes: 6

Views: 2214

Answers (2)

raphox
raphox

Reputation: 119

As @sergio said, the ParamsWrapper maps the available attribute based on the attribute_names method. So you can change the default behavior of ParamsWrapper, using the following code:

class UsersController < ApplicationController
  before_action :set_user, only: %i[ show update destroy ]

  wrap_parameters :user, include: [:email_address, :password, :password_confirmation]

  # POST /users
  def create
    @user = User.new(user_params)

    if @user.save
      render json: @user, status: :created, location: @user
    else
      render json: @user.errors, status: :unprocessable_entity
    end
  end

  #...

  private
    #...

    # Only allow a list of trusted parameters through.
    def user_params
      params.expect(user: [ :email_address, :password, :password_confirmation ])
    end
end

Upvotes: 0

Sergio Tulentsev
Sergio Tulentsev

Reputation: 230366

but I cannot use strong params to create a User with a Post request, and I don't know why.

Mainly because you're not using strong params correctly.

params.require(:user).permit(:username, :password)

This means that you need to pass {user: {username: 'foo', password: 'bar'}} instead of {username: 'foo', password: 'bar'} (see the nesting?).

But even though you don't pass params[:user][:username], it is somehow magically there. How? ParamsWrapper, that's how.

If you enable ParamsWrapper for :json format, instead of having to send JSON parameters like this:

{"user": {"name": "Konata"}}

You can send parameters like this:

{"name": "Konata"}

Yes, but why password doesn't get the same treatment? We find out a few lines below:

On Active Record models with no :include or :exclude option set, it will only wrap the parameters returned by the class method attribute_names.

password is not an attribute/column in your model, so params wrapper ignores it.

Upvotes: 15

Related Questions