I'm new to rails and i want to make a signup login and forget password functionality from scratch so i have been following railscast 250 and 274 episodes..so the problem is, the validations on password and password confirmation fields are working fine on signup form but on the reset password form those validations are not working at all..code is below
model:
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation, :signup_token
attr_accessor :password
before_save :encrypt_password
before_create {generate_token(:auth_token)}
before_create {generate_token(:signup_token)}
validates :password, length: {minimum: 8}, confirmation: true, presence: true,:on => :create
validates :email, presence: true, uniqueness: true
validates :email, format: {with: /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, message: "not valid"}
def generate_token(column)
begin
self[column] = SecureRandom.urlsafe_base64
end while User.exists?(column => self[column])
end
def self.authenticate(email, password)
user = find_by_email(email)
if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt) && user.active == true
user
else
nil
end
end
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
end
end
def send_password_reset
generate_token(:password_reset_token)
self.password_reset_sent_at = Time.zone.now
save!
UserMailer.password_reset(self).deliver
end
end
password_resets_controller:
class PasswordResetsController < ApplicationController
def new
end
def create
user = User.find_by_email(params[:email])
user.send_password_reset if user
redirect_to root_url, :notice => "Email sent with password reset instructions."
end
def edit
#user = User.find_by_password_reset_token!(params[:id])
end
def update
#user = User.find_by_password_reset_token!(params[:id])
if #user.password_reset_sent_at < 2.hours.ago
redirect_to new_password_reset_path, :alert => "Password reset has expired."
elsif #user.update_attributes(params[:user])
redirect_to root_url, :notice => "Password has been reset."
else
render :edit
end
end
end
and views under password reset are
new.html.erb:
<h1>Reset Password</h1>
<%= form_tag password_resets_path, :method => :post do %>
<div class="field">
<%= label_tag :email %>
<%= text_field_tag :email, params[:email] %>
</div>
<div class="actions"><%= submit_tag "Reset Password" %></div>
<% end %>
edit.html.erb:
<h1>Reset Password</h1>
<%= form_for #user, :url => password_reset_path(params[:id]) do |f| %>
<% if #user.errors.any? %>
<div class="error_messages">
<h2>Form is invalid</h2>
<ul>
<% for message in #user.errors.full_messages %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<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 class="actions"><%= f.submit "Update Password" %></div>
<% end %>
Related
Playing around with authentication from scratch and for some reason my users email address is not being saved to the DB. When I fire up the console and pull a random user, their email is always nil, but have validations set for the presence of email and am get a successful user created message. This was working before and not sure what broke it. I know this is something super simple but it has got me for the last few hours and am hoping a second pair of eyes can tell me what I'm doing wrong. GitHub link below
[https://github.com/smarandache1990/auth][1]
<h1>Sign Up</h1>
<%= form_for #user do |f| %>
<% if #user.errors.any? %>
<div class="error_messages">
<h2>Form is invalid</h2>
<ul>
<% for message in #user.errors.full_messages %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :email %><br />
<%= f.text_field :email %>
<% #user.errors.full_messages_for(:email).each do |message| %>
<div><%= message %></div>
<% end %>
</p>
<p>
<%= f.label :password %><br />
<%= f.password_field :password %>
<% #user.errors.full_messages_for(:password).each do |message| %>
<div><%= message %></div>
<% end %>
</p>
<p>
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %>
<% #user.errors.full_messages_for(:password_confirmation).each do |message| %>
<div><%= message %></div>
<% end %>
</p>
<p class="button"><%= f.submit %></p>
<% end %>
<h1>Sign In</h1>
<%= form_tag sessions_path do %>
<p>
<%= label_tag :email %><br />
<%= text_field_tag :email, params[:email] %>
</p>
<p>
<%= label_tag :password %><br />
<%= password_field_tag :password %>
</p>
<p class="button"><%= submit_tag "Log in" %></p>
<% end %>
class User < ApplicationRecord
attr_accessor :email, :password, :password_confirmation
before_save :encrypt_password
validates :password, confirmation: true, on: :create
validates :password_confirmation, presence: true
#validates_presence_of :password, :on => :create
#validates_confirmation_of :password
validates_presence_of :email
validates_uniqueness_of :email
def self.authenticate(email, password)
user = find_by_email(email)
if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
user
else
nil
end
end
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
end
end
end
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
redirect_to log_in_path, :notice => "Signed up!"
else
render :new, status: :unprocessable_entity
end
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end
class SessionsController < ApplicationController
def new
end
def create
#user = User.authenticate(params[:email], params[:password])
if #user
session[:user_id] = #user.id
redirect_to root_url, :notice => "Logged in!"
else
flash.now[:notice] = "Wrong username or password"
render :new, status: :unprocessable_entity
end
end
def destroy
session[:user_id] = nil
redirect_to root_url, :notice => "Logged out!"
end
end
Remove the email from the attr_acessor (here).
attr_accessor creates the getters and setters for all attributes listed there. So, basically, you're overriding these methods for the email attribute.
About the validation, use this:
validates :email, presence: true, uniqueness: true
I trying to authentication from scratch in rails 5 and my user information is not being saved when entered into the signup form. I also receive this rails error: ActiveModel::ForbiddenAttributesError
#user = User.new(params[:user])
class User < ApplicationRecord
attr_accessor :password
before_save :encrypt_password
validates_confirmation_of :password
validates_presence_of :password, :on => :create
validates_presence_of :email
validates_uniqueness_of :email
def self.authenticate(email, password)
user = find_by_email(email)
if user && user.password_hash == BCrypt::Engine.hash_secret(password,
user.password_salt)
user
else
nil
end
end
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password,
password_salt)
end
end
end
<h1>Sign Up</h1>
<%= form_for #user do |f| %>
<% if #user.errors.any? %>
<div class="error_messages">
<h2>Form is invalid</h2>
<ul>
<% for message in #user.errors.full_messages %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :email %><br />
<%= f.text_field :email %>
</p>
<p>
<%= f.label :password %><br />
<%= f.password_field :password %>
</p>
<p>
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %>
</p>
<p class="button"><%= f.submit %></p>
<% end %>
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
redirect_to root_url, :notice => "Signed up!"
else
render "new"
end
end
end
class Post < ApplicationRecord
has_secure_password
end
if you create from scratch make sure for each controller you have strong parameter declaration, let me explain, inside create method you put User.new(user_params), user_params is another method that we put usually on bottom of class, we put the method def user_params, this we declare what allowed field / data that can be passed to our model since you using bcrypt gem, I think the field is same as below
additional info most of rails user using devise gem for authorization user
class UsersController < ApplicationController
def create
#user = User.new(user_params)
# ...
end
private
def user_params
params.require(:user).permit(:username, :email, :password, :salt, :encrypted_password)
end
end
I'm working through Railscast: authentication from scratch in Rails 4, though the tutorial was built for Rails 3.
For this reason, some changes have had to be made from the original tutorial instructions.
Tutorial: http://railscasts.com/episodes/250-authentication-from-scratch?autoplay=true
Current running into an error
param is missing or the value is empty: session
Thoughts:
I've compared the 2 controllers below. My change from the tutorial is moving the params into a session_params method.
I understand the error is because the hash being posted does not contain a hash like "session" => { }. But I don't have a deep enough understanding of Rails to solve.
Recommended controller (rails 3)
def create
user = User.authenticate(params[:email], params[:password])
if user
session[:user_id] = user.id
redirect_to root_url, :notice => "Logged in!"
else
flash.now.alert = "Invalid email or password"
render "new"
end
end
My controller (converted to Rails 4)
def create
user = User.authenticate(session_params)
if user
session[:user_id] = user.id
redirect_to root_url, :notice => "Logged in!"
else
flash.now.alert = "Invalid email or password"
render "new"
end
end
private
def session_params
params.require(:session).permit(:email, :password)
end
Up to this point I've only been using Devise for authentication, so a great thanks in advance if you can share any advice.
UPDATE
As requested, form to post session
<h1>Log in</h1>
<%= form_tag sessions_path do %>
<p>
<%= label_tag :email %> <br/>
<%= text_field_tag :email, params[:email] %>
</p>
<p>
<%= label_tag :password %> <br/>
<%= password_field_tag :password %>
</p>
<p class="button"> <%= submit_tag %> </p>
<% end %>
UPDATE 2
Also adding user.rb model
class User < ActiveRecord::Base
attr_accessor :password
before_save :encrypt_password
validates_confirmation_of :password
validates_presence_of :password, :on => :create
validates_presence_of :email
validates_uniqueness_of :email
def self.authenticate(email, password)
user = find_by_email(email)
if user && user.password_hash == BCrypt::Engine.hash_secret(password, password_salt)
user
end
end
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
end
end
end
Try this one:
in sessions_controller.rb
def create
#user = User.find_by(email: params[:session][:email].downcase)
if #user && #user.authenticate(params[:session][:password])
session[:user_id] = #user.id
flash[:success] = "#{#user.email}, Successfully Logged In"
redirect_to root_url, :notice => "Logged in!"
else
flash.now[:danger] = "Incorrect User/Password"
render 'new'
end
end
html form
<%= form_for :session, url: :sessions do |f| %>
<div class="field">
<%= f.label :email %>
<%= f.email_field :email %>
</div>
<div class="field">
<%= f.label :password %>
<%= f.password_field :password %>
</div>
<%= f.submit "Sign In", class: "button" %>
<% end %>
routes.rb
post 'sessions' => 'sessions#create'
User.rb
class User < ActiveRecord::Base
has_secure_password
validates_confirmation_of :password
validates_presence_of :password, :on => :create
validates_presence_of :email
validates_uniqueness_of :email
end
sign up page works fine but log in page password not correct then also form is logged in..pls help
This is my users.controller.rb which has new and create action
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
redirect_to '/users/index', :notice => 'Signed In!'
else
render 'new'
end
end
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end
This is my sessions.controller.rb which has new,create and destroy
def new
end
def create
user = User.authenticate(params[:email],[:password])
if user
session[:user_id]= user.id
redirect_to '/users/index', :notice => 'Logged In'
else
flash.now.alert = 'Invalid email or Password'
render 'new'
end
end
def destroy
session[:user_id]= nil
redirect_to root_url, :notice => 'Logged Out'
end
end
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
helper_method :current_user
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
end
This is my new.html.erb for users controller-Sign Up Page
<%= form_for #user do |f| %>
<% if #user.errors.any? %>
<div class = "error_messages">
<h2>Form is Invalid</h2>
<ul>
<% for message in #user.errors.full_messages %>
<li> <%= message %> </li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :email %> <br />
<%= f.text_field :email %>
</p>
<p>
<%= f.label :password %> <br />
<%= f.password_field :password %>
</p>
<p>
<%= f.label :password_confirmation %> <br />
<%= f.password_field :password_confirmation %>
</p>
<p class = button> <%= f.submit %> </p>
<% end %>
This is my new.html.erb for sessions controller(Log In Page)
<%= form_tag sessions_path do %>
<p>
<%= label_tag :email %> <br />
<%= text_field_tag :email, params[:email] %>
</p>
<p>
<%= label_tag :password %> <br />
<%= password_field_tag :password,params[:password] %>
</p>
<p class=> <%= submit_tag %> </p>
<% end %>
This is User Model
class User < ActiveRecord::Base
attr_accessor :password
before_save :encrypt_password
validates_confirmation_of :password
validates_presence_of :password, :on => :create
validates_presence_of :email
validates_uniqueness_of :email
def self.authenticate (email, password)
user = find_by_email(email)
if user && user.password_hash = BCrypt::Engine.hash_secret(password, user.password_salt)
user
else
nil
end
end
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
end
end
end
You've fallen into the classic "single equals vs double equals trap"
if user && user.password_hash = BCrypt::Engine.hash_secret(password, user.password_salt)
should be
if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
You compared password with single equal sign (=), which is not comparing rather assigning. So the condition returning true always.
Change your authenticate action into this:
def self.authenticate (email, password)
user = find_by_email(email)
hashed_password = BCrypt::Engine.hash_secret(password, user.password_salt)
if user && user.password_hash == hashed_password
user
else
nil
end
end
And you have typo in the following line in your Session Controller's create action:
user = User.authenticate(params[:email], params[:password])
I'm not sure how to validate the confirm password element, this code seems to work, but the validates confirmation of password doesn't seem to compare the passwords as expected, just need a hand getting used to Rails 4's new ways.
Sign Up View:
Sign Up
<%= form_for #user do |f| %>
<% if #user.errors.any? %>
<div class="error_messages">
<h2>Form is invalid</h2>
<ul>
<% for message in #user.errors.full_messages %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.text_field :email, class: "form-control", autofocus: "", required: "", placeholder: "Email address" %>
<%= f.password_field :password, class: "form-control", required: "", placeholder: "Password" %>
<%= f.password_field :password_confirmation, class: "form-control", required: "", placeholder: "Confirm Password" %><br/>
<p class="button"><%= submit_tag "Register", class: "btn btn-lg btn-primary btn-block" %></p>
<% end %>
</div>
Users Controller:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.create(user_params)
if #user.save
redirect_to root_url, :notice => "Signed up!"
else
render "new"
end
end
private
def user_params
params.require(:user).permit(:username, :email, :password, :passord_confirmation, :salt, :encrypted_password)
end
end
Users Model:
class User < ActiveRecord::Base
attr_accessor :password
before_save :encrypt_password
validates_presence_of :password, on: :create
validates :password, confirmation: :true
validates_presence_of :email
validates_uniqueness_of :email
def self.authenticate(email, password)
user = find_by_email(email)
if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
user
else
nil
end
end
private
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
end
end
end
It looks like your model's password confirmation may be off, unless you have your own private method confirmation
Try changing your model from validates :password, confirmation: :true to validates_confirmation_of :password
The API has some examples as well.
You need to make the password and password_confirmation accessible. For example:
attr_accessor :password
attr_accessible :password, :password_confirmation
validates :password, :presence => true,
:confirmation => true
Try adding that, and see if it makes a difference.