I've just started to the django and got a little lost in between various doumentations and outdated tutorials.
What I want to achieve is to create a simple dashboard application that also contains some backend scripts to integrate various apis most of them are open API's like weather API's , calendar API's etc just for learning.
But I need to create a login page for the application and current tutorials for the authentication mosule of django is a little bit cunfusing as it never gives me how to implemelent a login form to the auth backend. I need a little bir more simple documentation for that.
Is there anyone able to help me on that. I know the question is not code related but still any help will be appriceated. Most of findings from my previous searches are outdated and implementing those methods are often causes errors like non-existent templates etc...
Meanwhile I'll be also checking the offical documentation.
Thank you.
So it depends what view you use to handle login as there are two types of views Class based and function based. I use Function based so I shall explain you with that.
First you should set up an html page that will have the form to take the details of the username and password. I assume you know how to do that and how to handle the form in the html (if not please comment is shall explain you that too). So now the view that will handle the login authentication is below
from django.contrib.auth import login,authenticate
def login_page(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('any_page_you_want_to_send_the_user')
else:
return render(request, 'login.html')
So what the above code does is that it accepts a POST request that you send from your login form and then it gets the username and password from that form. Then it uses the django's authenticate function to authenticate the user. It then checks if the there is actually an user or it is None(doesn't exists). And if the condition is true, it logs in the current user.
Related
I am new to Django authentication and Authorisation , we have a login and signup implemented with ReactJS on the client side and the Django JWT authentication on the server side. Now, I want to take this to next level by adding authorisation aswell by making users from same organizations only must be able to view/read/write the data existing in the application. Every user will be identified using a specific domain name ex: #gmail.com,#xyz.com. Users from one domain must not have any access to the database of the other organizations.
I think we can achieve this with Permissions concept in Django but dont know how we can do it technically. Any ideas are well appreciated.
Thanks
There can be multiple approaches, but I mostly handle this kind of stuff in the views, you can filter out the user's email and then can compare inside the views.
just an example:
#api_view(['GET', 'POST'])
def hello_world(request, id):
user = User.objects.get(id=id)
if request.method == 'POST':
if user.email == re.match(r"... regex here ...", user): # regex
return Response({"message": "Got some data of the organization xyz!", "data": request.data})
else:
return Response({"message": "Not authorized for XYZ operations!", "data": request.data})
This feels simple but understandable approach for me. And most often I follow this.
I have seen a few codes where sometimes the authenticate method have the request argument while other times it does not, e.g. see the below codes:
Code 1:
def register(request):
if request.method == 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
form.save()
# code for automatic login after singing up
username = form.cleaned_data['username']
password = form.cleaned_data['password1']
user = authenticate(username=username, password=password)
login(request, user)
return redirect('/')
Code 2:
def login(request):
if request.method == 'POST':
username = request.POST['username']
password = request.POST['password']
user = authenticate(request, username=username, password=password)
According to the official docs:
request is an HttpRequest and maybe None if it wasn’t provided to
authenticate() (which passes it on to the backend).
What does this request argument actually do and when should I consider passing it in the authenticate method?
According to the Django docs, and confirmed by me just now looking at the source, this is passed to the authenticate method of whatever authentication backend you are using.
The only backend class that seems to use it is the RemoteUserBackend, and the reason for this is because RemoteUserBackend will create a local User model instance the first time a user (who already exists in the remote auth database) logs in, and then, before returning the user instance, the backend calls its own configure_user method, passing the new user instance, along with request.
Docs for that here: https://docs.djangoproject.com/en/3.1/ref/contrib/auth/#django.contrib.auth.backends.RemoteUserBackend
But, here's where it gets silly.. is that, the default implementation of configure_user doesn't actually do anything, it's just a stub that you can extend in a subclass of RemoteUserBackend that you may choose to write if you want to add your own behavior to it.
Clear as mud. But really it makes sense. Perhaps some parameters were passed in the request that triggered the need for authentication, and some of those parameters contain information about the user that needs be stored in the newly created User instance... The request object contains everything known about the request, it comes in handy.
Suffice to say that somebody, some time, either wrote an auth backend that required access to the request at this point, or the author of the auth module anticipated that someone might need it some day. But in the auth module itself, by default, request is not used for anything that I can see.
So it doesn't seem necessary to pass request to authenticate, but if you can imagine that in the future you might want to configure your application with a funky authentication backend, you might want to pass it.. just in case it's needed by that backend. The default ModelBackend doesn't use it.
The context for your question is that django is a web server. In the classic HTTP/1 world, django receives a request and returns a response. The request argument typically represents the http request that is sent from the user's browser to the server to which django will be responding.
So, in the first function in your code, register appears to be intended to be used as part of a view to register a new user. The request, in this case, is useful to allow the register function to access the values that were entered in by the user in registering that user with the website. The same goes for login. The only specific reason that these function accept a request is that it allows the function to evaluate and take some action with respect to the information that the user submitted in the request.
I am using DRF with auth toolkit and it is working fine. However, I want to have a second login api so a user can log in using username and pin number. It is cos we have a USSD application and it is easier to give them a pin based login system.
Currently, I have the following URL that, when called, generates token:
url(r'^signin/', include('oauth2_provider.urls', namespace='oauth2_provider')),
For the ussd app, I want something like that but the auth2 should check pin field, defined in a separate model defined as follows:
class Members(models.Model):
pin=models.IntegerField()
user=models.ForeignKey(User)
I am a little lost where to start.
Using this answer as a base to answer this question, and Django's documentation.
I would say you'd want to create a custom authentication backend, and you'd want a custom user model with two passwords, or using a one-to-one relationship to add the additional password field, something like so:
from django.contrib.auth.models import AbstractBaseUser
class UserExtension(AbstractBaseUser):
user = models.OneToOneField(User)
...
Inheriting from the AbstractBaseUser should add a password field like the user model, (although I haven't tried this). If you prefer the custom user approach, I actually have a github repo that has a custom user app, so if you'd like to get any ideas of how to achieve this check it out.
Or have a look through the documentation.
Either way, once you've got your two passwords, you need to decide which one to use as the pin. If you're using oauth for the pin field and the web applicaiton with the password, I would probably use the standard user password for the pin login, as that way you don't need to change the oauth package to work with your new password. Then for your web application build a custom login. To do this create a custom authentication backend along the lines of:
from django.contrib.auth.models import User
from django.contrib.auth.hashers import check_password
class AuthBackend(object):
supports_object_permissions = True
supports_anonymous_user = False
supports_inactive_user = False
def get_user(self, user_id):
return User.objects.filter(pk=user_id).first()
def authenticate(self, username, password):
user = User.objects.filter(username=username).first()
if not user:
return None
# this is checking the password provided against the secondary password field
return user if check_password(password, user.userextension.password) else None
Then you need to add this authentication backend to your settings:
AUTHENTICATION_BACKENDS = ('myapp.backends.AuthBackend',)
Then create the web application login (as per the stackoverflow answer above):
from django.contrib.auth import authenticate, login
def my_login_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.
...
You should now have a custom authentication login for the web application using your password2 field, and you can use the oauth authentication to work with the standard Django password in which you're going to store the pin. Which I think is what you're trying to do?
NOTE: All of the above I haven't tested, so this may not work perfectly, but it should hopefully be able to at least point you in the right direction and give you a few ideas. If I'm understanding your problem correctly, this is the sort of approach that I would take to tackle the problem.
I am trying to make a website, where people only put their email addresses and they are logged in with cookies and all. At a later stage, i will ask them provide password and names, but NO username will be used. I am trying to do this with django-registraition, but i get errors and i have a few problems.
First to disable usernames as a login feature, i put str(time()) instead of username - i was looking for something that will change every time.
However, when I skip the authentication (which i currently don't need) i get error:
'RegistrationProfile' object has no attribute 'backend'
Alternatively, i can leave the authentication but then i don't know how to authenticate it only with email and no password. Also, i don't know how to make the next line work:
auth.login(request, ProfileUser)
If anyone can get me out of here, it would be awesome. Here is some code:
my form Class:
class RegistrationFormCustom(forms.Form):
email = forms.EmailField()
def do_save(self):
new_u = User(username=str(time()),email= self.cleaned_data.get('email'),)
new_u.save()
new_p = Profile.objects.create_profile(new_u)
new_p.save()
return new_p
my view:
def registerCustom(request, backend, success_url=None, form_class=None,
disallowed_url='registration_disallowed',
template_name='registration/registration_form.html',
extra_context=None,
initial={}):
form = RegistrationFormCustom(initial=initial)
if request.method == 'POST':
form = RegistrationFormCustom(initial=initial, data=request.POST)
if form.is_valid():
profile = form.do_save()
profile = auth.authenticate(username = profile.user.email, password = form.cleaned_data.get('pass1'))
print(profile)
auth.login(request, profile)
return redirect('/')
else:
pass
return render_jinja(request, 'registration/registration_form.html',
type="register",
form = form
)
and i will post any other snipped required happily
You're getting the 'RegistrationProfile' object has no attribute 'backend' error because the user is not yet authenticated. To log someone in, you have to call the authenticate method first, which requires a password. So, what you can do instead, is this:
from django.contrib.auth import load_backend, login, logout
from django.conf import settings
def _login_user(request, user):
"""
Log in a user without requiring credentials (using ``login`` from
``django.contrib.auth``, first finding a matching backend).
"""
if not hasattr(user, 'backend'):
for backend in settings.AUTHENTICATION_BACKENDS:
if user == load_backend(backend).get_user(user.pk):
user.backend = backend
break
if hasattr(user, 'backend'):
return login(request, user)
Then, to log someone in, just call the _login_user function with the request and User model. (This will be profile.user in your case, probably) Do this instead of calling auth.login. I'm not sure on how you're going to determine whether this is a valid user or not, without a password or username, but I'll leave that to you. If you still have trouble, let me know.
Short Explanation:
What basically happens here is that Django requires a user to be authenticated in order to be logged in via the login function. That authentication is usually done by the authenticate function, which requires a username and password, and checks whether the supplied password matches the hashed version in the database. If it does, it adds an authentication backend to the User model.
So, since you don't have a password and username, you just have to write your own method for adding the authentication backend to the User model. And that's what my _login_user) function does - if the user is already authenticated, it just calls login, otherwise, it first adds the default backend to the User model, without checking for a correct username and password (like authenticate does).
For others reading this thread, I got a similar error message when I was using User.objects.create() instead of User.objects.create_user(). Basically, the first method was setting a clear password whereas create_user encrypts the password. Clear passwords will fail to authenticate. Check your database, if you have passwords set in the clear, then it's likely you need to use create_user() instead.
The author's request could be fixed by simply setting a default user and password using create_user() instead of just user.save().
You can create a known password (put it in settings.py ) and use that as though the user entered it. Create the user with this and authenticate the user with this.
I'm trying to set up a custom backend that queries another database, for which I have created a model in the system. It uses its own rules (email instead of username, and a differently salted/hashed password) so I can't use built in authentication. I've set up a custom authentication backend like so:
class BlahBlahBackend:
def check_password():
# check password code here
return true
def authenticate(self, email=None, password=None):
import myapp.models.loginmodel
try:
person = myapp.models.loginmodel.People.objects.get(email=email)
if check_password(password, person.password):
try:
user = User.objects.get(email=email)
except User.DoesNotExist:
username=person.first_name + person.last_name
name_count = User.objects.filter(username__startswith = username).count()
if name_count:
username = '%s%s'%(username, name_count + 1)
user = User.objects.create_user(username,email)
else:
user = User.objects.create_user(username,email)
except People.DoesNotExist:
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
I've added BlahBlahBackend as an authentication backend:
AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend',
'socialauth.auth_backends.OpenIdBackend',
'socialauth.auth_backends.TwitterBackend',
'socialauth.auth_backends.FacebookBackend',
'socialauth.auth_backends.BlahBlahBackend',
)
As you can see, I'm also using some pre-existing auth backends that are also in socialauth.
I have a submission form that points to the following view:
def blahblah_login_complete(request):
email = request.POST.get('email')
password = request.POST.get('password')
user = authenticate(email,password)
# if user is authenticated then login user
if user:
login(request, user)
else:
return HttpResponseRedirect(reverse('socialauth_login_page'))
However, when I try to login in this way, it seems like one or more of the other backends are acting as if I'm trying to log in using their method.
I read that backends are cached and so ran
Session.objects.all().delete()
to clear out the backends cache.
My main questions are:
Does the order in which items are listed in AUTHENTICATION_BACKENDS
How does the system decide/know which Backend to use? This was never made clear by any of the documentation, and I find it a bit confusing.
Is there any way to force the use of a specific authorization based on the request. In other words, if someone submits a form, is there a way to force them to use the form-login-based authentication as opposed to the login via openid or Twitter?
Update:
It works! This is very cool, thanks. I guess it just seemed like the django doc was saying "You don't have to do anything else, it just sort of works like magic" and it turns out this is absolutely the case. So long as the backend is there and the credentials are set up correctly, the authentication will work. As it turns out the real problem was a misconfiguration in the urls.py file that wasn't sending the post from the login form to the correct handler, which is why it kept trying to use another authentication method.
You're supposed to use keyword arguments to django.contrib.auth.authenticate() The names should match the names of the arguments in your backend's authenticate method. The default backend handles the names 'username' & 'password'.
Your backend can use a different name for the keyword arguments e.g.: blahblah_email and blahblah_password, and then call authenticate(blahblah_email=..., blahblah_password=...).
It's clearly described here -
django tries each backend in order
defined, if first fails to
authenticate it goes to second etc.
I believe you can load backend class dynamically and authenticate
directly through it. Look at django authenticate() function sources on how to do that.
I guess django-cas will be a good reference for you :)
And yes, the order of AUTHENTICATION_BACKENDS matters.
Django loops over the backends list and stop at the first backend that has a authenticate method accepting the credential parameters you passed to it.