Ulises
Ulises

Reputation: 416

Devise Rails Password

I have a new application that the root user create another users, when i create the user with the model.

 @user = User.new(:email => params[:email], :password => params[:password], :password_confirmation => params[:password_confirmation])
 @user.save!

The users saves "correctly" on the table on "encrypted_password" the value and the others fields too, but when i try to login i have an error "Invalid Email or Password" so when i read the record the password is.

encrypted_password: $2a$11$wFnpiA.l9HezNXfnAGkttuu2IGIXByETytLrEkdDsa8sBFrc8Bdmq

But i used another password the root password that actually works. But on the table is :

 encrypted_password: $2a$11$VKOAUk5pjILU1QHYmkpJSem9KKm70QJPS7Oj.nPM/pTuyu1tqZaQO

So, my model of the devise is not saving the correct encryption of the password.

My TestController:

 class UsersController < ApplicationController
  before_action :authenticate_user!

  def index
    @Users = User.all
  end

 def create
     begin
        @user = User.new(:email => params[:email], :password => params[:password_first], :password_confirmation => params[:password_confirmation])
        @user.save!
        flash[:notice] = "Usuario creado correctamente"
        redirect_to action: 'index'  
     rescue Exception => e          
          flash[:alert] = "Error al crear al Usuario: " + e.message
          redirect_to action: 'index'  
     end
 end

end

What am i doing wrong?

Regards

Upvotes: 1

Views: 566

Answers (2)

max
max

Reputation: 102016

Start with a regular decent Rails crud setup and see if the issue does not solve itself.

# routes.rb
resources :users, only: [:create, :index]

class UsersController < ApplicationController

  before_action :authenticate_user!

  # GET /users
  def index
    @Users = User.all
  end

  # POST /users
  def create
    @user = User.new(user_params)
    if @user.save
      flash[:success] = "Usuario creado correctamente"
      redirect_to action: :index
    else
      render :new
    end
  end

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

# app/views/users/_form.html.erb
<% form_for(@user || User.new) do |f| %>
  <% if f.object.errors.any? %>
  <div id="error_explanation">
    <h2><%= pluralize(f.object.errors.count, "error") %> prohibited this article from being saved:</h2>
    <ul>
      <% f.object.errors.errors.full_messages.each do |msg| %>
      <li><%= msg %></li>
      <% end %>
    </ul>
  </div>
  <% end %>

  <div class="field">
    <%= f.label :email %>
    <%= f.email_field :email %>
  </div>
  <div class="field">
    <%= f.label :password %>
    <%= f.password_field :password %>
  </div>
  <div class="field">
    <%= f.label :password_confirmation %>
    <%= f.password_field :password_confirmation %>
  </div>
</div>
<% end %>

# app/views/users/new.html.erb
<%= render partial: 'form' %>

You can embed the partial in your root view or the users index by:

<%= render partial: 'users/form' %>

Note that you should render and not redirect if the record is invalid. If you redirect the users input and any validation messages you want to show are gone.

Upvotes: 0

tadman
tadman

Reputation: 211580

BCrypt goes out of its way to scramble the password as much as possible to make it extremely difficult to reverse that hashing operation. For any given input string to BCrypt::Password.create there are around 680 trillion-trillion-trillion possible output strings (2128), so it's unlikely you'll ever get the same one twice:

hashes = 10.times.map { BCrypt::Password.create('test') }
# => [
#   "$2a$10$vMrgjJHqvwnEKIs0fZ76pO3gbWL/0C3ExqK9HOpi/mHYu2.4GAO2K",
#   "$2a$10$KxBOarDzRPHp7QF1GGqNnuplRs1B5rNVfp21IHx1/HzQ0YIcIkLRW",
#   "$2a$10$emCdZAA.GU8GwQZkeJLfAuUTY2aEnhFmZ.GQAhDpJ.JGSh/m6s/k2",
#   "$2a$10$6R6xmGyK7Tb1MKsQb00vpOJKwpi56aj98JLoBJhBN4vWSQb7zagQm",
#   "$2a$10$r4qmb.C.vm88pL2nJK5TdOaWIboYaO6a1xHIRH.QDER6qYR6Ajvo.",
#   "$2a$10$mlVWz4IHTgYHSf3tAgEgpenpDHtGWYev4EUENLs7hnLlm6ikPhUxy",
#   "$2a$10$ixXdZZuc9rIVAozO8tyq5.wlsVOWBc6QWetNh3PvjPj2pGlqh.XOy",
#   "$2a$10$zLzuevtOl.g4RbaHpdeTZ.k4qjE/1m4nh6gN4mhcIKQPSa5sBcG5u",
#   "$2a$10$F/F71.DYEuzxS4W0w5m/a.IRpaVJxeh9sKUJ7DyQb5xU3SvFu1Ib.",
#   "$2a$10$ILXg8R52ZtHHbQbT0FxSFOj8YNqpNLmrH.6FhM3RGMwIuBeP1YXHa" ]

This is alright though since the verification routine can handle checking password:

hashes.map { |h| BCrypt::Password.new(h) == 'test' }
# => [true, true, true, true, true, true, true, true, true, true]

They all match. It's important to note that verifying ten passwords does take a small but noticeable amount of time. This is what make BCrypt particularly well suited to password storage: Guessing is expensive so throwing huge dictionaries of words at a hash to see which match is extremely difficult. This is not the case with weaker hashes like MD5 or SHA1 where a million operations per second is completely feasible. BCrypt is deliberately about a half million times slower.

Upvotes: 1

Related Questions