I'm experimenting with Django and I tried to create a login/register application. Here's where I've stuck - I'm able to register users, but then I can't login with them. Here's the code I think is relevant:
views.py
def login(request, template='accounts/sign_in.html'):
if request.user.is_authenticated():
return redirect(reverse('games'))
if request.method == 'POST':
post = request.POST.copy()
if 'password' in post:
post['password'] = make_password(post['password'])
form = AuthenticationForm(data=post)
if form.is_valid():
login(request, form.get_user())
messages.success(
request, "Successfully logged in.", extra_tags='success')
return redirect(reverse('games'))
else:
messages.warning(
request, "Wrong username or password." + request.POST['username'] + " " + request.POST['password'], extra_tags='error')
return redirect(reverse('login'))
return views.login(request, template)
def register(request, template='accounts/sign_up.html'):
if request.user.is_authenticated():
return redirect(reverse('home'))
if request.method == 'POST':
form = RegisterForm(request.POST)
if form.is_valid():
new_user = User(
username=form.cleaned_data['username'],
password=make_password(form.cleaned_data['password1']),
is_active=True,
)
new_user.save()
messages.success(request, "Your account was successfully created.")
return redirect(reverse('games'))
else:
form = RegisterForm()
return render(request, template, {'register_form': form})
When I try to log in with a user I've created (username: qwe, password: qweqweqwe), I get redjrected to login again, but the exact same username and password are printed in the message:
Wrong username or password.qwe qweqweqwe
However, when I try the interactive shell, here's what I get:
>>> User.objects.all()
[<User: admin>, <User: asd>, <User: qwe>]
>>> User.objects.all()[2]
<User: qwe>
>>> User.objects.all()[2].password
u'pbkdf2_sha256$10000$HM2k6uDntJ68$DLqHKcGxtJG7pJC7tbZcm29vB88LEgaw2xroqZEkTFw='
So I have such a user and it's a valid account.
I'm pretty sure you don't need to call make_password in this one:
if 'password' in post:
post['password'] = make_password(post['password'])
form = AuthenticationForm(data=post)
Just pass the normal request.POST to data and the form itself do the encryption and test it against the database. This is the fragment of AuthenticationForm's clean method where it do this:
def clean(self):
username = self.cleaned_data.get('username')
password = self.cleaned_data.get('password')
if username and password:
self.user_cache = authenticate(username=username,
password=password)
...
You can see the whole definition here.
I suppose your using official make_password function from django.contrib,auth but take a look at the docs closely, normally, user register functions do this automatically so you won't have to do it.
Anyways:
Check the errors form is yielding after is_valid call and change your login code to this:
if request.method == 'POST':
form = AuthenticationForm(data=request.POST)
Hope this helps!
Issue no. 1 - wrong login()
This is probably the problem:
login(request, form.get_user())
You already have a login function and it happens to be a view. I suppose you want to confirm user authentication (form.is_valid() does not do that automatically).
More details are in the documentation of auth module:
How to log a user in
If you have an authenticated user you want to attach to the current
session - this is done with a login() function.
login()
To log a user in, from a view, use login(). It takes an HttpRequest
object and a User object. login() saves the user’s ID in the session,
using Django’s session framework.
Note that any data set during the anonymous session is retained in the
session after a user logs in.
This example shows how you might use both authenticate() and login():
from django.contrib.auth import authenticate, login
def my_view(request):
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
# Redirect to a success page.
else:
# Return a 'disabled account' error message
else:
# Return an 'invalid login' error message.
Issue no. 2 - wrong password
As Paulo mentioned, AuthenticationForm already handles password hashing. Please read the official documentation for examples:
Using the Django authentication system: Authentication in Web requests
Related
I have a question.
1. in Django I created a login page after login successfully. If I enter /accounts/login again I can see this url and I can login again.
How can disable login page after that user was login and if user enter url for login page get another page?
views.py
def user_login(request):
if request.method == "POST":
username = request.POST.get('username')
password = request.POST.get('password')
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
return redirect("home")
else :
messages.error(request, "Bad username or password")
return render(request, "login.html", context={})
tnx for help
You can "disable" it, by adding a check that verifies if the user is already authenticated, and then redirect it to somewhere else, like:
def user_login(request):
if request.user.is_authenticated:
return redirect('home')
if request.method == "POST":
username = request.POST.get('username')
password = request.POST.get('password')
user = authenticate(request,username=username,password=password)
if user is not None:
login(request,user)
return redirect("home")
else :
messages.error(request,"Bad username or password")
return render(request,"login.html",context={})
So the if statement checks if the user is authenticated (logged in), and if so, then we return a HTTP redirect response to a view (here 'home').
In Django versions before django-1.10, the check is request.user.is_authenticated() (so the method has to be called), but since django-1.10 this is a property.
But I would not call it a (security) problem. One could also see it as a feature, and for instance log in to another account.
Django's authenticate function seems to always be returning none, ive already checked out this thread and updated the authentication backends in the settigns.py file.
This is the code that i am using to save the accounts being created:
if request.method == 'POST':
# Getting the information from the filled in UserCreationForm
user_form = UserCreationForm(data=request.POST)
# If the the form is valid then save the users data to the database
# hash the password using set_password and save the user again
# set registered to True
if user_form.is_valid():
user = user_form.save()
user.set_password(user.password)
user.save()
registered = True
# Invalid form or forms - mistakes or something else?
# Print problems to the terminal.
# They'll also be shown to the user.
else:
print user_form.errors
# Not a HTTP POST, so these forms will be blank, ready for user input.
else:
user_form = UserCreationForm()
# Render the template depending on the context.
return render_to_response(
'contracts/register.html',
{'user_form': user_form, 'registered': registered}, context)
This seems to be working, since after i create a new user on the site, the access database its linked to updates with that username and the encrypted password.
The user_from is created from UserCreationForm which is:
class UserCreationForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput())
class Meta:
model = User
fields = ('username','password','first_name','last_name','email','is_staff','is_active')
Here is the code in my user_login method which should be grabbing the username and password from the request and authenticating the combo
def user_login(request):
# context for the user's request.
context = RequestContext(request)
# form = AuthenticationForm()
# If the request is a HTTP POST
if request.method == 'POST':
# Gather the username and password provided by the user.
# This information is obtained from the login form.
user = request.user
userSubmit = user.username
passSubmit = user.password
#username = request.POST.get['username']
#password = request.POST.get['password']
user = authenticate(username = userSubmit, password=passSubmit)
#try:
# user = authenticate(username=userSubmit, password=passSubmit)
#except LockedOut:
# messages.error(request, 'You have been locked out because of too many login attempts. Please try again in 10 minutes.')
# If we have a User object, the details are correct.
# If None (Python's way of representing the absence of a value), no user
# with matching credentials was found.
else:
if user:
# Is the account active? It could have been disabled.
if user.is_active:
# If the account is valid and active, we can log the user in.
# We'll send the user back to the homepage.
login(request, user)
return redirect('home')
else:
# An inactive account was used - no logging in!
messages.error(request, 'Your account is disabled.')
else:
messages.error(request, 'The credentials you entered are invalid.')
# Bad login details were provided. So we can't log the user in.
# The request is not a HTTP POST, so display the login form.
# This scenario would most likely be a HTTP GET.
# No context variables to pass to the template system, hence the
# blank dictionary object...
return render_to_response('administrative/login.html', {'form': form}, context)
I have the following code and I get an error saying: "the User object has no attribute POST"
def login (request):
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(user)
return render(request, 'base_in/base_in.html', {})
else:
return render(request, 'signupapp/error.html', {'message':'the acount is not active'})
else:
return render(request, 'signupapp/error.html', {'message':'username and password is incorrect'})
I also tried this code and got another error: "login() takes 1 positional argument but 2 were given"
def login (request):
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(user)
return render(request, 'base_in/base_in.html', {})
else:
return render(request, 'signupapp/error.html', {'message':'the acount is not active'})
else:
return render(request, 'signupapp/error.html', {'message':'username and password is incorrect'})
What am I doing wrong? Based on django tutorials it should work properly:
https://docs.djangoproject.com/en/1.9/topics/auth/default/#how-to-log-a-user-in
What happened is you try to call login of from django.contrib.auth, but you are also defining your own function called login(), you have a kind of name conflict here.
You should rename that to something else, e.g. login_view()
from django.contrib.auth import authenticate, login
def login_view(request): # If you call it login,
# you get conflict with login imported aove
# The rest of your code here
# now if you call login(user), it will use the correct one,
# i.e. the one imported from django.contrib.auth
If you prefer not to rename, you can import Django's login under a different name, e.g.
from django.contrib.auth import login as auth_login
# Then use auth_login(user) in your code
I would advise adding a login form first
class LoginForm(forms.Form):
username = forms.CharField()
password = forms.CharField(widget=forms.PasswordInput)#hides password on input
then
from django.http import HttpResponseRedirect,HttpResponse
from django.contrib.auth import authenticate, login
.
.
def user_log(request):
#if you add request.method=='POST' is it a bug i dont know y
if request.method:
form = LoginForm(request.POST)
if form.is_valid():
cleandata=form.cleaned_data
#authenticate checks if credentials exists in db
user=authenticate(username=cleandata['username'],
password=cleandata['password'])
if user is not None:
if user.is_active:
login(request, user)
return HttpResponseRedirect("your template")
else:
return HttpResponseRedirect("your templlate")
else:
return HttpResponse("Invalid login")
else:
form=LoginForm()
return render(request, 'registration/login.html',{'form':form})
Im new to Django and I know that to redirect after login I have to set the parameter 'page'. But this only works when the login is successful. How can i do the same thing when some error occurs??
Ps: Im currently also using django-registration with simple backend
I think it's what you are looking for:
# Login
def connection(request):
# Redirect to dashboard if the user is log
if request.user.is_authenticated():
return redirect('YourProject.views.home')
# Control if a POST request has been sent.
if request.method == 'POST':
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None: #Verify form's content existence
if user.is_active: #Verify validity
login(request, user)
return redirect('/index') #It's ok, so go to index
else:
return redirect('/an_url/') #call the login view
return render(request, 'login.html', locals()) #You can remove local() it is for user's data viewing..
How can I log-in the user programmatically in Django? I have the username and password of the User. Is there a method that let's me log him in?
There is no other way than "programmatically". Of course, this is documented.
from django.contrib.auth import authenticate, login
user = authenticate(username=username, password=password)
if user is not None:
login(request, user)
Alsways be careful when programmatically logging users in, you might get the error ´user has no attribute "backend". You have to set the backend too if that has no happened previously. Project that uses this and some sample code:
def splash_register(request):
if request.session.get('beta'):
if request.method=='POST':
userform=MyUserCreationForm(request.POST)
if userform.is_valid():
#username of <30 char is required by Django User model. I'm storing username as a hash of user email
user=userform.save(commit=False)
user.username=hash(user.email)
user.backend='django.contrib.auth.backends.ModelBackend'
user.save()
username=user.username
password=str(userform.cleaned_data['password'])
auth.login(request, user)
request.session['first_visit']=True
return HttpResponseRedirect("/")
else:
userform=MyUserCreationForm(request.POST)
return render_to_response("website/splash_register.html", {'userform':userform}, context_instance=RequestContext(request))
return render_to_response("website/splash_register.html", context_instance=RequestContext(request))
else:
return HttpResponseRedirect('/splash/')
The accepted answer definitely works but, I prefer to use the Django built in auth forms, like django.contrib.auth.forms.AuthenticationForm
Here is a snippet that shows the important part
form = AuthenticationForm(request, data=request.POST)
if form.is_valid():
try:
form.clean()
except ValidationError:
# handle error
login(request, form.get_user())
The major difference in this approach is that AuthenticationForm.clean method calls authentication function for you and checks User.is_active for you as well.