Create a custom authentication - python

I'm transferring a database to a new project and more precisely the users.
Don't ask me why but the passwords in the old database were hashed with md5 and then with sha256.
I'm using django-rest-auth to manage login.
url(r'^api/rest-auth/', include('rest_auth.urls')),
I added a custom authentication method:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'users.auth.OldCustomAuthentication',
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
)
}
Here is my auth file:
class OldCustomAuthentication(BaseAuthentication):
def authenticate(self, request):
try:
password = request.POST['password']
email = request.POST['email']
except MultiValueDictKeyError:
return None
if not password or not email:
return None
password = hashlib.md5(password.encode())
password = hashlib.sha256(password.hexdigest().encode())
try:
user = User.objects.get(email=email, password=password.hexdigest())
except User.DoesNotExist:
return None
# User is found every time
print('FOUND USER', user)
return user, None
But I still get an error when I request http://apiUrl/rest-auth/login/:
{
"non_field_errors": [
"Unable to log in with provided credentials."
]
}
Do you have any idea? Or maybe I'm doing it in a wrong way.
Thank you in advance.
Jeremy.

Following the advice of #MrName I managed to solve my issue.
So I deleted DEFAULT_AUTHENTICATION_CLASSES in my settings and added this:
REST_AUTH_SERIALIZERS = {
'LOGIN_SERIALIZER': 'users.auth.LoginSerializer'
}
Then I copy pasted the original serializer and modified the function _validate_email with:
def _validate_email(self, email, password):
user = None
if email and password:
user = self.authenticate(email=email, password=password)
# TODO: REMOVE ONCE ALL USERS HAVE BEEN TRANSFERED TO THE NEW SYSTEM
if user is None:
password_hashed = hashlib.md5(password.encode())
password_hashed = hashlib.sha256(password_hashed.hexdigest().encode())
try:
user = User.objects.get(email=email, password=password_hashed.hexdigest())
except ObjectDoesNotExist:
user = None
else:
msg = _('Must include "email" and "password".')
raise exceptions.ValidationError(msg)
return user

Related

Custom User Model, Custom Authentication not working

I am a beginner in Django and I am working on a project which requires Custom user model as I Don't require is_staff, is_superuser, is_admin.
So, but searching and other ways I made my own Custom user model. But it is not working and I am stuck on it for days.
It will be a huge help if someone can help me with the code.
settings.py
AUTH_USER_MODEL = 'accounts.Usermanagement'
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
'accounts.backends.EmailAuthBackend',
]
backends.py
#backends.py
# from django.contrib.auth.models import User
from django.contrib.auth.hashers import check_password
from django.contrib.auth import get_user_model
Usermanagement = get_user_model()
class EmailAuthBackend:
def authenticate(self,request,username=None,password=None):
print(request)
try:
user = Usermanagement.objects.get(emailid=username)
print(password)
print(user.password)
# print(check_password(password))
# print(user.check_password(password))
if user.check_password(password):
return user
return None
except user.DoesNotExist:
return None
def get_user(self,user_id):
try:
return user.objects.get(pk=user_id)
except user.DoesNotExist:
return None
views.py
# views.py
def loginPage(request):
# POST
if request.method == 'POST':
form = AuthenticationForm(request,data=request.POST)
# loginPage.html the html tag has attribute name = username for email ,
# name = password for password
if form.is_valid(): # Form Valid
email = request.POST['username']
password = request.POST['password']
#Check
print("EMAIL: ",email)
print("PASSWORD: ",password)
# Authentication USER
user = authenticate(request,username=email,password=password)
print("Authenticated ",user) # Check
# check
print(user)
if user is not None: # If User found
login(request,user,backend='accounts.backends.EmailAuthBackend')
messages.info(request, f"You are now logged in as {email}.")
return redirect("home")
else: # If User Not found
messages.error(request,"User not found")
return HttpResponse("User not found, not able to login")
else: # Form InValid
messages.error(request,"Invalid username or password.")
return HttpResponse("Form Invalid")
# GET
else:
form = AuthenticationForm()
context = {"form":form}
return render(request,"loginPage.html",context=context)
urls.py and other configurations are correct.
Problems:
check_password : always False
In DB I have unencrypted password ( ex:- password=admin )
DB is a legacy(existing) DB , so I first made the DB and then I did "python manage.py inspectdb" , which created models for me and then I changed few things, but did not changed the field names or the db_table name.
I am very much ok to create user through SHELL.
In loginPage.html the html tag has attribute name = username for email , name = password for password
if any other requirements I will edit the Questions
i think you cannot use the check_password function in your case, because it has been created for encrypted password case. Just make a user.password == password in your EmailAuthBackend
For second problem with get_user(), I think your userId is not the primary key for Django maybe, problem probably can be solved by filter directly by userId:
def get_user(self, user_id):
try:
return user.objects.get(userId=user_id)
except User.DoesNotExist:
return None
Concerning the problem No.2
Remove the password=password in your user=self.model()
It's making it difficult to make reference to the password in your user.set_password()

Login with Email and Password in Django

I Want to login using Email and Password in Djnago so I took help from Youtube and Other StackOverflow's answers but It can't work.
Problem : I can't find out any problem in my code(given below) but IT IS NOT WORKING!!
backends.py(inside 'account' named app)
from django.contrib.auth.models import User
class EmailBackend(object):
def authenticate(self, username=None, password=None, **kwargs):
try:
user = User.object.get(email=username)
except User.DoesNotExist:
return None
else:
if user.check_password(password):
return user
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
views.py(inside 'page' named app)
def login(request):
if request.method == "POST":
email = request.POST['email']
password = request.POST['password']
user = authenticate(username=email, password=password)
if user is not None:
print("You are Logged in")
else:
print("Please Enter Valid Email or Password")
return render(request, "page/login.html")
settings.py
AUTHENTICATION_BACKENDS = [
'account.backends.EmailBackend'
]
Note: It works good with default username and password authentication.
Please help me to solve this problem..
Thank You!!
Ohh!! I get it.
It's because I hed to add 'request' parameter in my authentication method.
Right code is given below
backends.py(inside 'account' named app)
class EmailBackend(object):
def authenticate(self, request, username=None, password=None, **kwargs):
try:
user = User.objects.get(email=username)
except User.DoesNotExist:
return None
else:
if user.check_password(password):
return user
else:
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
Now it is work fine!!
By default, django.contrib.auth.urls will create a log in page from this pattern:
(r'^login/$', 'django.contrib.auth.views.login'),
You need to avoid/override this URL.
E.g.
Create a new login URL in your urls.py:
(r'^emaillogin/$', 'email_login_view'),

Problem with custom authentication backend

I am trying to make a custom authentication for my django project.I am using a custom user model by subclassing AbstractUser. Now after creating a superuser account and trying to login from admin page, i am getting the error Please enter the correct username and password for a staff account. Note that both fields may be case-sensitive..I've double checked that my username and password are right.
After doing some research, found out that something is wrong with the custom authentication backend i am making.
from support.models import CustomUser
class UsernameIdModelBackend(object):
def authenticate(self,username,password,uid):
if username:
try:
user = CustomUser.objects.get(username=username)
if user.check_password(password):
return user
except CustomUser.DoesNotExist:
return None
else:
try:
user= CustomUser.objects.get(uid=uid)
return user
except CustomUser.DoesNotExist:
return None
def get_user(self, user_id):
try:
return CustomUser.objects.get(pk=user_id)
except CustomUser.DoesNotExist:
return None
Ive also set AUTHENTICATION_BACKENDS in settings.py. What should be changed to correct this.Can someone please point me the right way.
PS. iam new to custom authentication and is looking forward to create one with 2 types of login.
Rather than doing it like this, you can just add Authentication Backend for login with uid, like this(as per documentation):
class UsernameIdModelBackend(object):
def authenticate(self, request, uid=None):
try:
user= CustomUser.objects.get(uid=uid)
return user
except CustomUser.DoesNotExist:
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
And add it to AUTHENTICATION_BACKENDS like this:
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
'your_path_to.backend.UsernameIdModelBackend',
)

Django authenticate() resets password

I have a weird problem here. When I login, it works well as expected but when I try to logout and try to login again, it says that my password is invalid.
I checked my User table and it's really changing my password everytime I use authenticate() function.
I got this error a month ago (still django 1.8 at that time) but gone after many testing and tracing and praying and didn't have a single idea what happened. It only occurs in my local machine though.
authentication.py
after authenticate() function, my password is already changed (tried to put a breakpoint after the function so I know for sure that this is the culprit).
from django.contrib.auth import authenticate, logout, login
def signin(request):
if request.method == "POST":
result = {}
data = req_data(request)
try:
user = authenticate(username = data['email'], password = data['password'])
if user:
if user.is_active:
login(request, user)
#return success for redirection
else:
raise ValueError("This user is inactive. Please contact your admin.")
else:
raise ValueError("Invalid username/password.")
except Exception as e:
return HttpResponse(e, status = 400)
else:
return redirect("login")
def signout(request):
logout(request)
return redirect("login")
#gets the params from ajax post
def req_data(request):
return json.loads(request.body.decode("utf-8")) if request.body.decode("utf-8") else {}
I checked the DB and got this result.
Old Password
pbkdf2_sha256$20000$N4esMaOT5BYi$nIehHw63b+iZSz2Vmu1hEO10BqPfzAGu1cZA1ci/nXI=
New Password (After login)
pbkdf2_sha256$24000$KVZeuG4pgSkv$VIenbuq0Wk8sYZros4kE4Q7W0Jt+bOC23ha4/VSOXV8=
EDIT:
for the meantime, I am not using authenticate() and just use a generic password.
username = data.get('email',"")
password = data.get('password',"")
if password == "genericpassword123":
try:
user = User.objects.get(email = username)
user.backend = 'django.contrib.auth.backends.ModelBackend'
except User.DoesNotExist:
raise ValueError("Invalid username/password.")
else:
user = authenticate(username = username, password = password)
if user:
if user.is_active:
login(request, user)
#return success for redirection
else:
raise ValueError("This user is inactive. Please contact your admin.")
else:
raise ValueError("Invalid username/password.")
Python 2.7
Django 1.9
Postgre 9.4
Thanks!
It's working now. I never thought that it works in different account with different password.

Django Password reset: new password must be different from any of the last four passwords user has submitted

I am new to Django, please help me how to implement Password Reset so that new password must be different from any of the last four passwords user has submitted in django :
I am using default django password reset:
urls.py:
url(r'^password/reset/$',auth_views.password_reset,
name='auth_password_reset'),
django/contrib/auth/views.py:
#csrf_protect
def password_reset(request, is_admin_site=False,
template_name='registration/password_reset_form.html',
email_template_name='registration/password_reset_email.html',
subject_template_name='registration/password_reset_subject.txt',
password_reset_form=PasswordResetForm,
token_generator=default_token_generator,
post_reset_redirect=None,
from_email=None,
current_app=None,
extra_context=None):
if post_reset_redirect is None:
post_reset_redirect = reverse('django.contrib.auth.views.password_reset_done')
if request.method == "POST":
form = password_reset_form(request.POST)
if form.is_valid():
opts = {
'use_https': request.is_secure(),
'token_generator': token_generator,
'from_email': from_email,
'email_template_name': email_template_name,
'subject_template_name': subject_template_name,
'request': request,
}
if is_admin_site:
opts = dict(opts, domain_override=request.get_host())
form.save(**opts)
return HttpResponseRedirect(post_reset_redirect)
else:
form = password_reset_form()
context = {
'form': form,
}
if extra_context is not None:
context.update(extra_context)
return TemplateResponse(request, template_name, context,
current_app=current_app)
Should I create database table to store all the previous passwords of the user or is there any django library which will provide me this feature.
Yes, you can create a model and store all the passwords of a user whenever password reset/changed.
from django.contrib.auth.models import User
import json
class OldPasswords(models.Model):
user = model.ForeignKey(User)
pwd = models.CharField(max_length=200)
def setPasswords(self, pwd):
self.pwd = json.dumps(pwd)
def getPasswords(self):
return json.loads(self.pwd)
create a signal while password reset/changed and save current password.
for example:
from allauth.account.signals import password_changed, password_reset
def send_password_changed_email(sender, **kwargs):
user = kwargs.get('user')
if user:
pwds = OldPasswords.objects.get_or_create(user=user)
pwds.setPasswords(pwd)
After saving to the model, you need to implement custom validator to show validation message when user try the old password while reset:
validate(self, password, user=None): validate a password. Return None if the password is valid, or raise a ValidationError with an
error message if the password is not valid. You must be able to
deal with user being None - if that means your validator can’t run,
simply return None for no error.
get_help_text(): provide a help text to explain the requirements to the user.
Any items in the OPTIONS in AUTH_PASSWORD_VALIDATORS for your validator will be passed to the constructor. All constructor arguments should have a default value.
Here’s a basic example of a validator, with one optional setting:
from django.core.exceptions import ValidationError
from yourapp.model import OldPasswords
from django.utils.translation import ugettext as _
class PasswordValidator(object):
def __init__(self, password):
self.password = password
def validate(self, password, user=None):
pwd_list = OldPasswords.objects.get(user=user).getPasswords()
if password in pwd_list:
raise ValidationError(
_("You used this password recently. Please choose a different one."),
code='password_recently_used',
params={'min_length': self.min_length},
)
def get_help_text(self):
return _(
"You used this password recently. Please choose a different one."
)
However, if you decide to store a user’s previous passwords, you should never do so in clear text.

Categories