Django : intercept authentication to store session variable or cookie - django

I am working on a project that's running on django. I would like to authenticate in multiple places. First, I would like to maintain the standard authentication mechanism and continue using it for site administration. Second, I would like to intercept the login request in addition to the standard authentication, and check if the user has is authenticated on another system and store a session variable or cookie to be used later if authenticated. Then on logout remove the session variable or cookie. The second authentication mechanism should not affect the first. In addition, if the first succeeds and the second fails, it should have no affect on the standard administration.
I have looked into declaring a custom authentication backend in the settings AUTHENTICATION_BACKENDS tuple. But from what I understand, it authenticates in order and will stop authenticating once a match is made.
Any guidance in regards to this would be most appreciated. Thanks

If all you need is set and unset cookie or session var you can use signals sent by authentication module (docs)
Example:
from django.contrib.auth.signals import user_logged_in, user_logged_out
def user_logged_in_hook(sender, **kwargs):
# kwargs will contain request and user objects
print kwargs
def user_logged_out_hook(sender, **kwargs):
# kwargs will contain request and user objects
print kwargs
user_logged_in.connect(user_logged_in_hook)
user_logged_out.connect(user_logged_out_hook)

Related

How to implement admin login and logout using Django REST framework?

I have been given a task to authenticate admin login programmatically and logout as well.
I am able to to do login but on logged out when I check which user I am logging out it says AnonymousUser. How can I make sure I log out current user which is logged it.
I am using Django REST framework and testing it on Postman.
#api_view(["POST"])
def adminLogin(request):
if(request.method=="POST"):
username = request.data["username"]
password = request.data["password"]
authenticated_user = authenticate(request,username=username, password=password)
if authenticated_user != None:
if(authenticated_user.is_authenticated and authenticated_user.is_superuser):
login(request,authenticated_user)
return JsonResponse({"Message":"User is Authenticated. "})
else:
return JsonResponse({"message":"User is not authenticated. "})
else:
return JsonResponse({"Message":"Either User is not registered or password does not match"})
#api_view(["POST"])
def adminLogout(request):
print(request.user)
logout(request)
return JsonResponse({"message":"LoggedOut"})
Logging in/logging out with a REST API makes not much sense. The idea of logging in/logging out, at least how Django implements it, is by means of the session, so with a cookie that has the session id.
API clients like Postman usually do not work with cookies: each request is made more or less independent of the previous one. If you thus make the next request without a reference to the session, then the view will not link a user to that request. Clients like AJAX that runs on the browser of course can work with cookies, since these are embedded in the browser that manages cookies. You can work with cookies in postman as specified in this tutorial [learning postman], but this is usually not how an API is supposed to work.
This is why APIs usually work with a token, for example a JWT token. When authenticating, these are given a token that might be valid for a short amount of time, and subsequently it uses that token to make any other request that should be authorized.
As the Django REST framework documentation on TokenAuthentication [drf-doc] says, you can define views that create, and revoke tokens. The page also discusses session authentication that thus can be used for AJAX requests.
But likely you are thus using the wrong means to do proper authentication for your REST API, and you thus might want to work with a token like a JWT token instead.

How to authenticate a Django session using captcha?

I have built a website, and a captcha. The captcha is generated by models and displayed on a template.
Views
from resumesite.models import Chess_board
import json
def home(request):
return render(request, 'home.html', {})
def chess(request):
board = Chess_board()
data = mark_safe(json.dumps(board.rep))
return render(request, 'captcha_original.html',{'board': data})
I would like to redirect all requests to the captcha, and on completion of the captcha redirect to website and allow full access for duration of the session (i.e. for period of 20 minutes). How would you suggest going about this?
Options
Middleware/decorator authenticating by ip address (I have read this won't work if user is using a proxy)
Custom login form, with decorator #login_required(login_url="/chess/")
Integrating with REST and using token authentication
You would have to use a server-side session to handle the access. By using the server-side session you can set an expiry. In the session store, a variable called access=True, which you can check in the other function if it exists and serve pages. After the expiry time hits, this session is deleted and function will see a None value.
For more information read following docs
https://docs.djangoproject.com/en/3.0/topics/http/sessions/#configuring-the-session-engine

Django sessions for anonymous users

I want to be able to collect basic stats on the use of a webapp by users, both anonymous and logged-in.
The commonality here would be that using session ids, I could store data for both logged-in and logged-out users, and still be able to link the stored stats to a given session (who the session belongs to is immaterial).
However, I'm running into issues with collecting the session_key, as this does not appear to be set when an anonymous user enters the site (presumably because of the fact Django sessions are only saved when modified.
When I test a view with a logged-in user:
def create(request, *args, **kwargs):
print request.session.session_key
For a logged in user, the session_key is printed. For a logged out user or anonymous user this is None. On first request to the site, the session does not exist and consequently is not available to the view.
My current plan is to create a custom Middleware as a subclass of the official session middleware, but overriding process_request() to instantiate sessions for those who do not have one via session.save().
My only concern with this approach is that I'm not sure if it will have unforeseen consequences for other parts of Django - do people have any suggestions?
In a past project I did what you are suggesting but just within a view where I needed to use session_key for unauthenticated visitors. It did not cause any problems in my project:
if not request.session or not request.session.session_key:
request.session.save()
# request.session.session_key now set
You can choose to save session every request by setting:
SESSION_SAVE_EVERY_REQUEST = True
This force django to assign the session key for each session
https://docs.djangoproject.com/en/2.1/topics/http/sessions/#when-sessions-are-saved

Django: How to destroy user session after a password reset/change?

i've recently implemented a simple change password view in my django project. The thing is that the old session should be destroyed for security reasons. What's the best way of doing this without asking the user to log in again.
I think i could just logout/login him/her, something like this:
from django.contrib.auth import login as auth_login
from django.contrib.auth import logout as auth_logout
#login_required
def change_password(request):
# My stuff
request.user.set_password(new_password)
request.user.save()
# I need this:
logout(request)
login(request,request.user)
But i think this is not the best idea. What do you think?
Is there another way to do this?
Am I missing something? (I mean, is this secure)
Take a look at this app https://github.com/atugushev/django-password-session.
This package makes invalidated all sessions (except a current session) after change a password.
Also this feature finally was implemented in Django 1.7. See: https://docs.djangoproject.com/en/dev/topics/auth/default/#session-invalidation-on-password-change
I just found out that this is now a built-in feature of Django, and has been since 1.7:
https://docs.djangoproject.com/en/1.7/topics/auth/default/#session-invalidation-on-password-change
Essentially, all sessions now include a hash of the users' password, so if the user ever changes their password, all their existing sessions are automatically invalidated.
So, the short answer to your question is: upgrade django.
One possibly undesirable side effect of this change is that, by default, a user ends up having to log in again as soon as they change their password. So you probably actually want the current user session to stay logged in. See the docs already linked, Django's built-in views for password change do that for you default, or you can manually call a function called update_session_auth_hash
django clears the session on logout so you will be fine:
https://docs.djangoproject.com/en/dev/topics/auth/#django.contrib.auth.logout
When you call logout(), the session data for the current request is completely cleaned out. All existing data is removed. This is to prevent another person from using the same Web browser to log in and have access to the previous user's session data.
I don't understand whats are these security reasons that forces to reset session. But, the way is:
#login_required
def change_password(request):
request.user.set_password(new_password)
request.user.save()
username = request.user.username
logout(request)
user = authenticate(username=username, password=new_password) #<-- here!!
if user is not None:
login(request,user)
else:
#raise your exception
you should authenticate before login. Quoting doc:
Calling authenticate() first When you're manually logging a user in,
you must call authenticate() before you call login(). authenticate()
sets an attribute on the User noting which authentication backend
successfully authenticated that user (see the backends documentation
for details), and this information is needed later during the login
process.

how to access the request in a django custom authentication backend?

I want to do the following with django's authentication:
Log incorrect log-in attempts
Temporarily lock accounts after 'x' number of incorrect log-in attempts
Log successful log-ins.
I thought a custom auth backend would be the solution.
I can do most of what i want, but I want to log the IP and REMOTE_HOST of the user making the attempt.
how can I access the request object in the auth backend?
Thanks
The authentication backend can take any number of custom parameters for the authenticate() method. For example:
class MyBackend:
def authenticate(self, username=None, password=None, request=None):
# check username, password
if request is not None:
# log values from request object
If you are calling authenticate in your own view, you can pass the request object:
from django.contrib.auth import authenticate
def login(request):
# discover username and password
authenticate(username=username, password=password, request=request)
# continue as normal
If you're using django's login view (or the admin login), you wont have the extra information. Put simply, you'll have to use your own custom login view.
Also, be careful when automatically locking accounts: you allow someone to deliberately lock one of your user's accounts (denial of service). There are ways around this. Also, make sure your log of incorrect attempts doesn't contain any attempted passwords.
In recent versions of Django, authenticate() accepts "request" as first parameter:
optionally since Django 1.1
required since Django 2.1
See:
https://docs.djangoproject.com/en/2.1/releases/1.11/#deprecated-features-1-11
https://docs.djangoproject.com/en/2.1/releases/2.1/