I'm trying to perform django authentication using a custom user model. Note this is for a school project and not
I have the following User model
class User(AbstractBaseUser):
userID = models.AutoField(primary_key=True)
username = models.CharField(max_length=20, unique=True)
password = models.CharField(max_length=24)
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=50)
USERNAME_FIELD = "username"
REQUIRED_FIELDS = ["first_name", "last_name"]
def get_full_name(self):
return self.first_name + " " + self.last_name
def get_short_name(self):
return self.username
class Meta:
db_table = "User"
app_label = "funbids"
managed = False
I have my model defined in settings.py
AUTH_USER_MODEL = 'funbids.User'
I'm also using a custom auth backend. Note this is just a test using cleartext passwords (I'm aware this is a terrible idea to do in any production environment). I'm using a custom auth backend because a requirement for this app is to authenticate using raw SQL queries with an existing database.
class AuthBackend(object):
"""
Authenticate a user in funbids
"""
def authenticate(self, request, username=None, password=None):
# Test credentials
cursor = connections["funbids"].cursor()
cursor.execute("SELECT 1 FROM User WHERE username=%s AND password=%s", [username, password])
if cursor.fetchone():
# Have to grab the model, then authenticate
user = User.objects.get(username=username)
return user
else:
return None
def get_user(self, user_id):
try:
return User.objects.get(username=user_id)
except User.DoesNotExist:
return None
Everything seems to work in my login view.
def login_user(request, login_failed=False):
# Redirect the user to the index if they're already authenticated and arrive at the login page
if request.user.is_authenticated:
return redirect("funbids:index")
# Get the username and password from POST data
username = request.POST.get("username", "")
password = request.POST.get("password", "")
next = request.POST.get("next", "")
# Attempt to authenticate the user if both a username and password are present
if username and password:
log.debug("User %s requesting login" % username)
# Test credentials
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
log.debug("authenticated user is: %s" % request.user)
request.session.set_expiry(46800)
else:
login_failed = True
if request.user.is_authenticated:
# Authentication succeeded. Send the user to the original page they requested
# using the the "next" POST data or the index.
log.debug("Successfully authenticated user %s" % request.user)
if next:
return redirect(next)
else:
return redirect("funbids:index")
else:
# Failed authenticate, send back to login page
log.debug("Failed to authenticate user %s" % username)
# No credentials present/user failed auth - just load the page and populate the "next" form input
# in case the user was redirected here from a view they couldn't access unauthenticated.
next = request.GET.get("next", "")
try:
template = loader.get_template("funbids/login.html")
except TemplateDoesNotExist:
raise Http404("Page Not Found")
context = {
"pagetitle": "Welcome to FunBids!",
"next": next,
"login_failed": login_failed,
"template": template,
"request": request,
}
# Return the rendered page for display.
return render(request, template_name="funbids/page.html", context=context)
The debug statement prints our the username perfectly, something like:
[DEBUG] Successfully authenticated user adam
HOWEVER
Once I switch to another view, I'm no longer logged in. Instead, I'm an anonymous user, for instance:
def search(request):
log.debug("request user is: %s" % request.user)
try:
template = loader.get_template("funbids/search.html")
except TemplateDoesNotExist:
raise Http404("Page Not Found")
context = {
"pagetitle": "Search items for sale",
"template": template,
"request": request,
}
# Return the rendered page for display.
return render(request, template_name="funbids/page.html", context=context)
This time the debug statement prints out:
[DEBUG] request user is: AnonymousUser
I've done some reading on the issue and found that this happens when a user authenticates, but doesn't login. However, I can login successfully without any issues, so I'm not sure what's going on.
Would appreciate any help...thanks.
It turns out the problem was very subtle. Django wasn't able to get my user because my get_user method was wrong in my auth backend.
return User.objects.get(username=user_id)
should be
return User.objects.get(userID=user_id)
as the "userID" field is the primary key.
Related
In my project I have an existing legacy database, so I use inspectdb to create models and for specific requirements I am using custom user model
No when I create a user directly in the DB using SQL commands then the user gets authenticate and is able to login, but when I create user using SHELL commands (model_name.objects.create()), then the user is not authenticated and is not able to login.
and the main point, ** when I create user using SQL command password is stored in RAW form, but when I create user using SHELL commands the password looks like
"pbkdf2_sha256---------------------------------"
managers.py
#managers.py
class UsermanagementCustomUserManager(BaseUserManager):
def create_user(self,emailid,firstname, password=None):
"""
Creates and saves a User with the given email, date of
birth and password.
"""
if not emailid:
raise ValueError('Users must have an email address')
user = self.model(
emailid=self.normalize_email(emailid),
password=password,
)
user.set_password(password)
user.save(using=self._db)
return user
backends.py
#backends.py
from django.contrib.auth.backends import BaseBackend
from django.contrib.auth.hashers import make_password,check_password
from django.contrib.auth import get_user_model
Usermanagement = get_user_model()
class EmailAuthBackend(BaseBackend):
def authenticate(self,request,username=None,password=None):
# print("Custom authenticate rqst: ",request)
try:
print("Trying the email backend!")
user = Usermanagement.objects.get(emailid=username)
print("Got the user")
# print(password)
# print(user.password)
# print(check_password(password))
# print(user.check_password(password))
if user.password == password or user.check_password(password):
return user
except user.DoesNotExist:
return None
def get_user(self,user_id):
try:
print("Getting the user of the Email Bkacned")
return Usermanagement.objects.get(pk=user_id)
except Usermanagement.DoesNotExist:
return None
views.py
#views.py
from django.contrib.auth import login,logout ,authenticate
from django.contrib.auth.forms import AuthenticationForm
def loginPage(request):
# POST
if request.method == 'POST':
form = AuthenticationForm(request,data=request.POST)
if form.is_valid():
email = form.cleaned_data.get('username')
password = form.cleaned_data.get('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.is_active:
login(request,user,backend='accounts.backends.EmailAuthBackend')
# messages.info(request, f"You are now logged in as {email}.")
return redirect("home")
else:
pass
else: # If User Not found
# messages.error(request,"User not found")
# return HttpResponse("User not found, not able to login")
pass
else: # Form InValid
# messages.error(request,"Invalid username or password.")
# return HttpResponse("Form Invalid")
context = {
"form" : form
}
return render(request,"loginPage.html",context=context)
# GET
else:
form = AuthenticationForm()
context = {"form":form}
return render(request,"loginPage.html",context=context)
The way I am authenticating my user is directly using
user.password == password
I am trying to understand login methods in Django. So because I want to login with email not username. I wrote a custom authentication backend and exposed it in settings.py
AUTHENTICATION_BACKENDS = (
'accounts.backend.EmailAuthenticationBackend',
'django.contrib.auth.backends.ModelBackend',
)
class EmailAuthenticationBackend(ModelBackend):
def authenticate(self, request, email=None, password=None, **kwargs):
try:
user = Account.objects.get(email=email)
if user.check_password(password):
return user
except user.DoesNotExist:
pass
And in view user_login how I authenticate the user. And it's actually works. I mean kind of...
def user_login(request):
next = request.GET.get("next")
form = UserLoginForm(request.POST or None)
if form.is_valid():
email = form.cleaned_data.get("email")
password = form.cleaned_data.get("password")
user = authenticate(email=email, password=password)
login(request, user)
if next:
return redirect(next)
return redirect("/")
context = {
"form": form
}
return render(request, "accounts/user_login.html", context)
the authenticate() method is returns the user. But redirect() is not working because in browser thinks user not logged in. I am not sure about how to handle the session part.
and custom UserLoginForm
class UserLoginForm(forms.Form):
email = forms.CharField()
password = forms.CharField()
def clean(self, *args, **kwargs):
email = self.cleaned_data.get("email")
password = self.cleaned_data.get("password")
if email and password:
user = authenticate(email=email, password=password)
if not user:
raise forms.ValidationError("user not exist")
if not user.check_password(password):
raise forms.ValidationError("incorrect password")
return super(UserLoginForm, self).clean(*args, **kwargs)
The second issue is if email or password wrong. it doesn't raise any error.
what is the best common way to handle custom login with email.
I think the reason, it doesn't redirect is you've assigned the
request.GET.get("next") into nxt variable, however you are
checking the next variable for redirecting, probably you should
change the variable nxt to next.
The reason that it doesn't raise any error is, you've put try and except to handle the thrown errors in the authenticate() method and you just put pass in the excpetion part. If you want the error to be thrown, just remove the try and except part from authenticate().
I building an password reset system for my users. An password reset code sending to user mail and now I want to authenticate user by this code. If user enter the right code then password will be change otherwise not.
I am also storing the verification code in my models fields.
models.py:
class UserProfile(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,related_name="userprofile")
forget_password_token = models.CharField(max_length=100,blank=True,null=True)
views.py I am sending the code to user mail and also storing the same code in my models fields
def ForgetPasswordSendCode(request):
if request.method == "POST":
email = request.POST["email"]
User = get_user_model()
if not User.objects.filter(email=email).first():
messages.success(request, "Invalid mail")
return redirect('members:reset-password')
user_obj = User.objects.get(email=email)
reset_code = str(rand_number_mail()) #generating random code
profile_obj = UserProfile.objects.get(user=user_obj)
profile_obj.forget_password_token = reset_code
profile_obj.save()
current_site = get_current_site(request)
subject = 'Verification Code'
context = {
'user_first_name': user_obj.first_name ,
'user_last_name': user_obj.last_name ,
'domain': current_site.domain,
'reset_code': reset_code
}
html_body = render_to_string('mail/resetpassword-mail.html', context)
to_email = request.POST["email"]
email = EmailMultiAlternatives(subject=subject,from_email='noreply#farhyn.com',to=[to_email])
email.attach_alternative(html_body, "text/html")
email.send(fail_silently=False)
messages.success(request, "An password reset code sent to your email")
return redirect('members:change-password') #redirecting user to password reset page after submitting mail.
return render(request, 'members/password_reset_form.html')
Now I am stuck in password reset view where user insert the code and change his password. I am not undersealing how to authenticate user by verification code.
def ChangePassWordPage(request):
return render(request,'members/password_change.html')
This might helps
Step1: Send user your code and code must have a reference of your user so it will be easy to cross check
Step2: if your code match with your user (this case act as a authentication )
Step3: update your user model with new password (make_password)
UPDATE
def ChangePassWordPage(request):
if request.method == "POST":
email = request.POST["email"]
user_token = request.POST["token"]
User = get_user_model()
if not User.objects.filter(email=email).first():
messages.success(request, "Invalid mail")
return redirect('members:reset-password')
user_obj = User.objects.get(email=email)
token = UserProfile.objects.filter(user = user_obj).first().forget_password_token
if token == user_token:
#update your user password
else:
return redirect('members:reset-password')
return render(request,'members/password_change.html')
In step 2, your token will act as authentication means, token will just verify the user and token to match and if that matches then you just update the password.
And this will authorized you to update your password
Yes Same as it is!!
You don't authenticate the user by the verification code. You get the matching user object by the code and chance the password. –
Klaus D.
Apologies if this is simple or my terminology is off, this is my first django project. I haven't been able to find a similar solution for this online.
I have an existing application, with a postgres DB where I authenticate my users. I have wrote an application in Django to interact with some tables and display info to the user. I would like to use Django to login and track User sessions against this DB. so I can use the features like
{% if user.is_authenticated %}
but I don't want to use the migrate command so I don't want to change the existing DB. I can access the table where the account info is as I created a model for it.
I see you can use remote user logon param but I cant find any sample or guide on how to use it and am completely lost.
Right now I create a login form in the views page. Then get the username and password that is entered, but I don't know what to do next. Also would need to hash the password. Is there a libray in djano that will do that for the app.
Any pointers or online guides for this would be appreciated.
Here is the views for login
if request.method == "POST":
form = LoginForm(request.POST)
if form.is_valid():
email = form.data['account_email']
password = form.data['account_password']
user = authenticate(username=email)
if user.check_password(password):
login(request, user)
return redirect('myapp:cust_select')
else:
# Username and password did not match
raise ValidationError('Invalid Username/Password')
return render(request, 'myapp/login.html', {'form' : form}
backends.py
from django.conf import settings
from django.contrib.auth import get_user_model
class UserAuthBackend(object):
def authenticate(self, username=None, password=None):
try:
account = get_user_model()
user = account.objects.get(account_email=username)
if user:
return user
except account.DoesNotExist:
print "account not found"
return None
def get_user(self, user_id):
try:
account = get_user_model()
return account.objects.get(pk=user_id)
except User.DoesNotExist:
return None
models.py
class Accounts(AbstractUser):
account_id = models.AutoField(primary_key=True)
account_email = models.CharField(max_length=100)
account_password = models.CharField(max_length=20)
def __str__(self):
return self.account_email
class Meta:
managed = False
db_table = 'accounts'
settings.py
AUTHENTICATION_BACKENDS = ( 'myapp.backends.UserAuthBackend', )
Its keeps exiting with the same error in the sql query.
column accounts.password does not exist
LINE 1: SELECT "accounts"."password", "accounts"."last_login", "acco...
It doesnt appear to be using my Account model. It does select it from that table but how can i get it to stop requesting accounts.password and accounts.last_login as they dont exist in y Accounts model
For reference
Note: You need to do try, catch to get this code working
def login(request):
form = LoginForm()
if request.method == "POST":
form = LoginForm(request.POST)
if form.is_valid():
username = form.data['account_email']
password = form.data['account_password']
# First authenticate
user = authenticate(request, username=username, password=password)
if user is not None :
# Succeed, now log user in
login(request,user)
return redirect('myapp:select')
else:
# Username and password did not match
raise ValidationError('Invalid Username/Password')
return render(request, 'myapp/login.html', {'form' : form})
I'm using a custom user model that extends AbstractBaseUser. This is the user model:
class cUser(AbstractBaseUser):
def save(self, *args, **kwargs):
pass
role_id = models.IntegerField()
user_id = models.IntegerField()
email = models.CharField(max_length=40)
password = models.CharField(max_length=40)
f_name = models.CharField(max_length=40)
l_name = models.CharField(max_length=40)
address_id = models.IntegerField()
phone_num = models.IntegerField()
loan_item_count = models.IntegerField()
id = models.IntegerField(unique = True, primary_key = True)
def __init__(self, dictionary, *args, **kwargs):
self.role_id = int(dictionary['role_id'])
self.user_id = dictionary['user_id']
self.email = dictionary['email']
self.password = dictionary['password']
self.f_name = dictionary['f_name']
self.l_name = dictionary['l_name']
self.address_id = dictionary['address_id']
self.phone_num = dictionary['phone_num']
self.loan_item_count = dictionary['loan_item_count']
self.id = self.user_id
USERNAME_FIELD = 'user_id'
class Meta:
managed = False
I don't want the model to affect the DB in any way. I'm loading it by a simple raw SQL query from a gateway method.
This is how I'm handling login:
def login_request(request):
if request.method == 'POST':
form = LoginForm(request.POST)
if form.is_valid():
username = request.POST['username']
password = request.POST['password']
user = userGateway(username,password)
if user is not None:
print("=========USER==========")
print(user.email)
user.backend = 'django.contrib.auth.backends.ModelBackend'
login(request,user)
print(request.user.is_authenticated)
if user.role_id==1:
return render(request, 'biblioteca/admin/landing.html')
# return HttpResponseRedirect('/')
else:
return render(request, 'biblioteca/landing.html')
else:
print("=========NOT USER==========")
else:
if(request.user is not None and not request.user.is_anonymous):
return render(request, 'biblioteca/admin/landing.html')
form = LoginForm()
return render(request, 'biblioteca/login.html', {'form': form})
As you can see, I'm setting the back-end before login to authenticate without having to pass through a password - the password check is being done when the user object is created by comparing the password passed in with the password retrieved from the DB.
If I return a render, as seen here, the next page contains the proper request.user object. If I return a redirect, it does not. Additionally, if I leave that page at all, the user becomes unauthenticated and anonymous again, losing the session.
Any help as to why this happens would be much appreciated.
When you redirect, the request is completed and a redirect code is sent to the client, who then makes a new request to the new page without the POST data for the login form. The user may not be getting their token. In that case, render a page to log them in that delays for a few seconds, then redirects them to the right spot.
So, this was solved by turning userGateway() into a proper auth backend and implementing it like that.
Explanation as to why:
The sessionID in django stores a hash of the user's PK as well as the auth backend used to log in. Whenever a new request is sent, data needed is lazy-loaded from the DB using this hash.
This is why login() was properly authenticating the user on the login page, because it was pretty much forced to. As soon as another request happened, though, it would be unable to load the user's data from the DB, and would flush the sessionid.