I'm trying to build a very simple login page with username and password authentication using BCrypt.
There are two features, the first is to create a username and password, and the second is authenticate using login page. (create a new user, login with that user)
Below is the controller and User model code for creating a username and password. View model is not included, but it includes a simple form asking the user to create a username, password and confirm password
Controller:
get '/new_user' do
erb :new_user
end
post '/new_user' do
if #password == #password_confirm
new_user = User.new(username: params[:username])
new_user.password = params[:password]
new_user.insert_user
redirect '/index'
else
redirect '/new_user'
end
end
Model:
def initialize(params = {})
#username = params.fetch(:username, "test")
#password = params.fetch(:password, "test")
end
def password=(new_password)
#password = BCrypt::Password.create(new_password)
#db_password = BCrypt::Password.new(#password)
end
def insert_user
db = SQLite3::Database.open("helper_database")
db_results_as_hash = true
db.execute("INSERT INTO users (username, password) VALUES (?,?)", [#username, #db_password])
end
In the above, a new instance is created to pass username and password. If password and confirm password match, the password method will create an encrypted password, and the insert method will insert username and the encrypted password into the database using simple SQL command.
After the username and encrypted password have been inserted into the database. I want to use the login page to validate. I create a new User instance, passing the username and password params to the authentication method shown below. The method will look up the password corresponding the params username and evaluate the params password against the encrypted password.
Below is the controller and User model for Login
Controller:
get '/login' do
erb :login
end
post '/login' do
#user = User.new(username: params[:username], password: params[:password])
if #user.authenticate()
redirect '/index'
else
erb :login
end
end
Model:
def authenticate
db = SQLite3::Database.open("helper_database")
db.results_as_hash = true
password = db.execute("SELECT password FROM users WHERE username = '#{#username}'")
password = password[0]["password"]
password = BCrypt::Password.new(password)
#password == password
end
Above, I'm attempting to retrieve the encrypted password (string format) from SQL, convert to a Bcrypt object and validate the password against it. Theoretically, the method should return true assuming the entered password is correct, but it returns false.
What might the issue be?
You don’t appear to be hashing the incoming password when you authenticate. This is because you’re doing #password == password where #password is the plain-text String password (from User#initialize from post '/login').
You should flip the comparison so it uses BCrypt::Password#==: password == #password.
Related
I created a form where logged-in users can change their email address. After the new email is saved there seems to be no matching query for the email/password combination. I checked the database entry for the specific user instance and the email was changed as expected. Even when I used django.contrib.auth.authenticate to match email/password it couldn't match. What am I doing wrong? Below is the form and view I used:
Forms.py
class UpdateEmailForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput())
new_email = forms.EmailField()
class Meta:
model = User
fields = ['new_email', 'password']
def clean(self):
cleaned_data = self.cleaned_data
old_password = cleaned_data.get('password')
if not check_password(old_password, self.instance.password):
raise forms.ValidationError('Incorrect password. Please try again.')
return cleaned_data
Views.py
def user_login(request):
form = LoginForm(request.POST)
context = {'form': form}
if request.POST.get('user_login'):
email = request.POST.get('email')
password = request.POST.get('password')
user = authenticate(email=email, password=password)
if user is not None:
login(request, user)
return redirect('profile')
else:
messages.error(request, 'Email and Password does not match.')
return redirect('login')
return render(request, 'users/login.html', context)
Below is where I handle the new email form data
#login_required
def profile(request):
...
elif request.POST.get('update_email'):
data = Customer.objects.get(email=request.user.email)
email_form = UpdateEmailForm(request.POST, instance=request.user)
if email_form.is_valid():
email_form.save()
email = request.POST.get('new_email')
user = User.objects.get(email=request.user.email)
user.email = email
data.email = email
data.save()
user.save()
messages.success(request, 'Your account has been updated successfully')
return redirect('profile')
else:
return render(request, 'users/profile.html', {'data': data, 'email_form': email_form})
I fixed the problem by creating a new field named confirm_password rather than using the actual field password. The password is changed or not saved when I save the form using the actual password field
Class UpdateEmailForm(forms.Forms.ModelForm):
...
confirm_password = models.CharField(widget=forms.PasswordInput())
class Meta:
fields = ['confirm_password', 'new_email']
When trying to register users with FusionAuth which do already have an Bcrypted password, which was originally generated with org.springframework.security.crypto.bcrypt.BCrypt (with 10 hashing rounds) the user is not able to use her password when trying to authenticate with FusionAuth any longer.
The hashed passwords look like:
$2a$10$3sTYd6sHbHIsTO2jBiishOTmcvFS.bJ2X2lbIDO1w2o4MNXXXXXXX
The import code (in Kotlin) looks like:
val response = client.register(null, RegistrationRequest(User().apply {
username = user.username
email = user.email
firstName = user.firstname
lastName = user.lastname
active = user.isActive
encryptionScheme = "bcrypt"
password = user.password // generated with BCrypt(10)
passwordChangeRequired = false
data.putAll(mapOf(
"mainMandant" to user.mainMandant?.mandantName,
"custodianMandants" to user.custodianMandants.joinToString { it.mandantName },
"technicalUser" to user.isTechnicalUser
))
}, UserRegistration().apply {
applicationId = myAppId
roles = extractUserRoles(user).toSortedSet()
preferredLanguages.add(Locale.GERMAN)
username = user.username
}, false, true))
With FusionAuth system settings "Cryptographic hash settings" is configured to use Scheme "Salted Bcrypt" with Factor 10 and re-hash on login enabled.
How to register an user and allow to re-use the existing password if possible?
The Registration API does not accept a hashed password, this API will assume the value provided in the password field is plain text.
In order to import a user with an existing hashed password, you will need to use the Import API.
https://fusionauth.io/docs/v1/tech/apis/users#import-users
Once you import the user, if you enable the re-hash at login the password hash will be update to match the configured values in the system configuration at next login.
I am trying to set up an email where a referrer will fill in some information in a form about a referee. The referee will receive an email to subscribe to my services with 2 different messages depending if he is already existing in the database or not (ghost status). But when the email is sent, the password is not automatically generated. Why ?
Email sent:
You have been registered as a teacher on XX.
Please confirm your account
Confirm my account
and connect with these credentials log in:<br />
login: bb#ee.fr<br />
password:
My controller:
def new_teacher_registered(teacher, user = nil)
#teacher = teacher
#user = user
#password = user.generate_password
mail(from: 'XX', to: teacher.email, bcc: "YY", subject: 'You have been registered on XYZ')
end
My view:
<% if #user.ghost? %>
You have been registered as a teacher on XXX.
Please confirm your account
<%= link_to "Confirm my account",
confirmation_url(#user, I18n.locale, :confirmation_token => #user.confirmation_token) %>
and connect with these credentials log in:<br />
login: <%= #user.email %> <br />
password: <%= #password %>
<%else%>
You have been registered on XX by <%= #teacher.studios.first.user.contact.location.name %>
And I have the generate_password method in my User controller
class User < ActiveRecord::Base
.....
def generate_password
unless self.encrypted_password
#password = Devise.friendly_token.first(8)
if self.update(password: password, password_confirmation: password)
#password
else
false
end
end
end
......
end
Instead of sending them an email with a password (not a best practice), why not send them a link where they can create their own password on your secure site?
The below example will create a reset link for them to click and give them a temporary password so that devise is happy, and then send an email with the link(the raw token).
raw_token, hashed_token = Devise.token_generator.generate(User, :reset_password_token)
self.reset_password_token = hashed_token
self.reset_password_sent_at = Time.now.utc
self.password = Devise.friendly_token.first(8)
self.save
UserMailer.add_to_website(course, self, raw_token).deliver_later
I'm quite new with DataMapper and Sinatra and especially attr_encrypted. What I want is to store my passwords encrypted and then be able to search for a user by username and password. I read the documentation of attr_encrypted, but I still don't get what to do :(
Can you please give my some example of a project using these two technologies or tell how to change my code to work :(
My User Class:
class User
include DataMapper::Resource
attr_encryptor :password, :key => 'secret key'
property :id, Serial
property :encrypted_password, Text
end
When I save a User, I do it this way:
username = params[:username]
password = params[:password]
user = User.new(:username => username, :encrypted_password => password)
user.save
which is saving the original password, not the encrypted one.
And I have no idea how to search for users when the password is encrypted :(
Now it is something like this:
#user = User.all(:username => username, :password => password)
Please excuse me for the newbie quiestion, but I really can't quite understand it :(
Thank you very much in advance!
You need to add the attr_encryptor line after you have specified the Data Mapper properties. This prevents DataMapper simply replacing the encrypted_password accessors with its own:
class User
include DataMapper::Resource
property :id, Serial
property :encrypted_password, Text
# this line moved down from above
attr_encryptor :password, :key => 'secret key'
end
and then create the user with:
user = User.new(:username => username, :password => password)
Are you sure you want to search for a User based on an encrypted password? Normally you would find the user based on e.g. username and then just check the password matches.
If you do want to do this, you will have to either recreate the encrypted password in your code and search with that (you’ll need to check the docs to see how encryption is done):
User.all(:username => username, :encrypted_password => encrypt(password))
Alternatively fetch all matching users and filter them in your code:
User.all(:username => name).select {|u| u.password == password}
Your encrypted password is :password, so you have to do
User.new(:username => username, :password => password)
To find an user by username and password, you should just do
User.first(:username => username, :password => password)
Anyway you could avoid that gem using instead bcrypt doing a thing like this.
I create a new user, approve them, then immediately attempt to authenticate with the credentials I just used to create the user...and get "false" back the Authenticate method.
string username = "me";
string password = "mypassword";
string email = "me#domain.com";
Membership.CreateUser(username, password, email);
currentUser = Membership.GetUser(username);
currentUser.IsApproved = true;
bool isAuthenticated = FormsAuthentication.Authenticate(username, password);
I checked the database table -- the user is created with that username, password, and email. According to the database table, they are approved.
Yet, the Authenticate method still returns false.
You need to call the UpdateUser method after you change their approved status...
Membership.UpdateUser(currentUser);
http://msdn.microsoft.com/en-us/library/system.web.security.membershipuser.isapproved.aspx
Likely that the authentication mechanism is using a different data store under the covers that has yet to be cascade to.