Search code examples
ruby-on-rails

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.


Solution

  • 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.