this is my first post on stack. I am just curious as I have read regarding password hashing and more often I was told to use BCrypt instead. However, are there any documentations that I can use in order for me to try and hash the passwords on my own? I know using BCrypt is optimal for safety reasons but I would like to play around and understand the codes or functions surrounding hashing a password when the user signs up without using BCrypt or any other gems.
For this, I have tried using 'SecureRandom' and 'Digest' but I still seem to be unable to get the desired output. Perhaps I am still new at this.
This is my codes. I am very new at this.
require 'digest'
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
redirect_to root_url
else
end
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, password_hash([:password]))
end
def password_hash(password)
hash = Digest::SHA256.hexdigest(password)
end
end
Once I convert it into strings, the password column in the database will be empty after the user sign up. I know my codes are a messy but I would like to try to encrypt the passwords without using gems.
My team always use Devise gem for work with users. It simple for using and have
great functional as: password encrypt, send email, authorization and other
Related
I am trying to build an API that deals with an existing mobile app. The mobile App uses username but it is really just an email and instead of reworking devise to have a confusing system of username which really is email. I wanted to use alias_attribute. It seems to work for everything but the login.
The JSON comes up as
{ "username":"test12345#gmail.com", "password":"password" }
and I permit the keys
devise_parameter_sanitizer.permit(:sign_in, keys: [:username, :password])
but it fails at
self.resource = warden.authenticate!(auth_options)
It seems like warden is ignoring the alias_attribute. If I change the username to email it works fine. I have tried overwriting the database auth function on the model but it never gets to that line.
from what I can tell, it's way easier to override the default sessions_controller behaviour
inside you sessions controller:
def create
if params[:user][:username]
params[:user][:email] = params[:user][:username]
end
super
end
or if yo don't want to overrride the create method then add
before_action :override_params, only: :create
def override_params
if params[:user][:username]
params[:user][:email] = params[:user][:username]
end
end
it's not nice, but should do the trick. And if you don't have to rewrite the insides of devise then I'd give it a go.
Otherwise you would have to do
add new authentication key to devise initializer
(inside config/initializers/devise.rb add config.authentication_keys = [:email, :username])
then you would have to override find_for_database_authnetication in User model to search the user by the username param value (check https://github.com/plataformatec/devise/blob/master/lib/devise/models/database_authenticatable.rb#L231). Because alias_attribute doesn't really help you in this scenario
probably some other steps as well
All in all, the dirty method seems simpler and more straightforward
EDIT:
also, checkout this page https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-sign_in-using-their-username-or-email-address
maybe it will be of some help
I would like to protect a site with a very simple password only validation when user first visits site. I currently use http authentication, but that requires a username & password. I can hardcode password in back end. Basics of site: local sports league where we keep our stats and info about league. Simply trying to keep "riff-raff" out :)
I am a ruby on rails newbie, and am using this site as a way to learn. Any help out there would be appreciated!
You could do something cookie-based.
In your ApplicationController, you'd implement a method for determining if the cookie is present that states that the visitor has entered your password – if the cookie isn't present, then you'll redirect to your password page:
class ApplicationController < ActionController::Base
def require_password_verification
unless cookies[:visitor_password_verified]
return redirect_to <whatever your passwords#new path is>
end
end
end
The controller for your password page would look something like this:
class PasswordController < ApplicationController
def new
# Nothing needed here because all your #new view needs is a password field
end
def create
unless params[:password].present?
return redirect_back(fallback_location: root_path, alert: 'Password is required.')
end
if params[:password] == Rails.configuration.visitor_password
cookies[:visitor_password_verified] = true
redirect_to(root_path, notice: 'Password verified.')
else
cookies.delete(:visitor_password_verified)
redirect_back(fallback_location: root_path, alert: 'You've entered the wrong password.')
end
end
end
Your password would be stored in the application.rb file, like so:
config.visitor_password = '12345'
Normally, you would never store a password in this way because it's not secure at all but considering your use case, it's probably fine, since having a single password for everybody is already not secure. 😃 However, if you did want to step up the security a notch, I would recommend storing your password in an environment variable, and then you could set the password like so:
config.visitor_password = ENV['VISITOR_PASSWORD']
That way, at least your password isn't hard-coded and accessible to anybody who looks at your, assumedly public, repo.
And then you can require the "password has been entered" cookie for whatever views you want like so:
class LeagueStatsController < ApplicationController
before_action :require_password_verification
def index
# Whatever
end
end
If somebody hits your league_stats#index page, then it's going to check to make sure the visitor_password_verified cookie is present and true first. If it is, then they'll get through to the view. If it's not, they'll be redirected to your passwords#new page.
I'm working on google authentication for a rails app. Currently using the omniauth-google-oauth2 gem to implement Google auth. I've managed to have users sign in using google. However, I'd also like users to be able to sign up using google. My problem is that I've matched the google callback URL to a particular controller action (sessions#create).
Is it possible to choose between 2 redirect URIs based on whether users are signing in or signing up? Currently, my only idea is to create new google client credentials to be used for sign up, I hope there is a better way.
You don't need to have 2 redirect uris, you just need to do some more work when receiving the callback. For instance:
class SessionsController < ApplicationController
...
def create
email = auth_hash['info']['email'] # assuming your omniauth hash is auth_hash and you're requiring the email scope
#user = User.find_by(email: email) if !email.blank? # assuming your user model is User
if #user
login_user(#user) # use your login method
elsif !email.blank?
#user = User.new(name: auth_hash['info']['name'], email: email)
unless #user.save!(validate: false) # validate false because I'm enforcing passwords on devise - hence I need to allow passwordless register here)
# deal with error on saving
end
else
# deal with no found user and no email
end
end
protected
def auth_hash
request.env['omniauth.auth']
end
end
I've written all steps but the creation process can be shortened to:
#user = User.create_with(name: auth_hash['info']['name']).find_or_initialize_by(email: email)
#user.save! if #user.new_record?
if #user
login_user(#user)
else
# deal with no user
end
Nonetheless, you can't be sure the user is going to give you scope access to the email, so personally I think the first version, even if a bit lengthier is more robust. Then on the shorter version there's also the problem of, if #user is false, why is so? And will require you to add more logic to figure out why is that, whereas in the first one it's much easier to apply the correct response to each situation.
I'm using FB and G+ for authentication. How do I make the first user that is registered an admin? I've looked at a few answers like this one, but I get errors (like "undefined method `update_attribute' for nil:NilClass" for the previous linked answer) and they are mostly old and for devise gem.
users_controller
def update
#user = User.find(params[:id])
if #user.update(user_params)
redirect_to #user
else
render 'edit'
end
end
private
def user_params
params.require(:user).permit(:admin, :banned)
end
This should be done in the db/seeds.rb file which you can execute with rake db:seed. The linking with Facebook and G+ will have to be done after the fact. I would not depend on any solution that automatically promotes the first user to admin status. That should only ever done explicitly.
Example seeds.rb:
# This example is using Devise, however it can easily be adapted to whatever
# setup you require as there is nothing "devise" specific here.
user = User.find_or_create_by(email: "Admin Email")
user.password = "Admin Password"
user.admin = true
user.save
Another tool I use is by registering a user normally and then manually promoting them to Admin status via a tool like rails console.
EDIT
If you still wish to pursue your method (which I consider unsafe) then you can use ActiveRecord hooks like so:
class User < ActiveRecord::Base
# relations and stuff
before_save :check_to_make_admin
private
def check_to_make_admin
# Only the first user should be made admin
if User.count == 0
self.admin = true
end
end
end
It's important to note that I strongly discourage this approach and recommned using the seed method which was created for purposes like this.
For the purpose of a new web app, I would need on my sign up page (which is administrator only) just only one email field.
The thing is that I'm totally new at rails and so even basics things like that are for me really difficult...
I created my authentification using Railscast #270 which uses has_secure_password method.
For now, everything works great except that I dont need all this bullcrap...
I also want to use Action Mailer to send the generated password to his email adress.
A hex(8) password would be perfect (I have seen SecureRandom but it seems to be depreciated)
Users_Controller:
class UsersController < ApplicationController
skip_before_filter :is_connected?, :only => [:new, :create]
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
# Tell the Mailer to send a welcome Email after save
Mailer.confirm_email(#user).deliver
redirect_to root_url, :notice => "Signed up!"
else
render "new"
end
end
end
User_model:
class User < ActiveRecord::Base
attr_accessible :email
has_secure_password
validates_presence_of :password, :email, :on => :create
end
For now, in my view, I have 2 fields. But as I said earlier, I only want one.
I would like to keep using has_secure_password which seems to offer a pretty good security regarding hash/salt.
Rails provides ActiveSupport::SecureRandom which either (depending on the Ruby version) is just a bridge to Ruby's SecureRandom or reimplemented it on older versions of Ruby (if my memory is correct SecureRandom was added in 1.8.7)
Now that all of the versions of Ruby that Rails supports have SecureRandom built-in ActiveSupport::SecureRandom is no longer needed and has been deprecated. SecureRandom itself is going nowhere -
require 'securerandom'
SecureRandom.hex(8)
should do fine (you might want to consider SecureRandom.urlsafe_base64 for a more compact representation of the same amount of actual randomness)
Here is one simple code for random password with lenth 8
rand_password=('0'..'z').to_a.shuffle.first(8).join
Hope it will help.
Sometimes things from Rails are deprecated because they duplicate functionality that has been added to Ruby core, and SecureRandom seems to be one of those things.
You can use any of those random generator methods to produce a one-time-use password.
To Create Random and unique token/password
class User < ActiveRecord::Base
before_create :generate_password
def generate_password
self.password = loop do
random_token = SecureRandom.urlsafe_base64
# If you are using FFaker gem then you can use it otherwise
# SecureRandom is great choice
# random_token = FFaker::Internet.password
break random_token unless User.exists?(password: random_token)
end
end
end
The main object here is to generate random token and do not repeat that token in the database. It can be really useful for some cases like generating unique token, unique invoice number, etc