A few days ago, I was implementing an auto-logout feature in my Django project using JavaScript. The goal was simple:
π If the user is inactive for some time, log them out automatically without them clicking anything.
Everything seemed to work... until I logged in again and suddenly got logged out again right after logging in π΅.
Hereβs what really happened β and how @login_required was silently involved.
π What I Was Trying To Do
I used Djangoβs built-in logout view inside my own logout view:
# python
from django.contrib.auth import logout
from django.contrib.auth.decorators import login_required
@login_required
def logout_user(request):
logout(request)
return redirect("Login:login")
On the front end, I wrote some JavaScript that checks for inactivity and then hits the logout endpoint:
javascript
fetch("/logout/")
.then(() => window.location.href = "/login/");
After setting this up, I noticed something strange:
π After the system auto-logged me out and redirected me to the login page, I entered my credentials β but right after that first login, I got logged out again immediately. Just that one time, right after auto-logout. π
π Debugging the Mystery Logout
I checked browser dev tools and noticed this:
GET /logout/?next=/logout/
Wait⦠what?! Why is /logout/ redirecting to itself?
π― The Culprit: @login_required Decorator
Here's how @login_required works under the hood:
- It checks if the request.user is authenticated.
- If not, it redirects to the login page, but with a query param:
/login/?next=/logout/
This is so that after logging in, Django knows where to send the user back.
But what happened here?
- My logout_user view had @login_required on it.
- When the auto-logout JS was triggered, the session was already expired.
- Django saw that the user was not logged in β so it redirected them to:
/login/?next=/logout/
- After logging in, Django redirected back to /logout/ because of next=/logout/.
- Now the user is logged in β hits the @login_required logout_user view β and gets logged out immediately again.
- And the cycle repeats π
π§© The Fix: Remove @login_required from Logout
Since logging out doesn't need login, it's perfectly fine to remove @login_required from the logout view.
from django.contrib.auth import logout
def logout_user(request):
logout(request)
return redirect("Login:login")
β Now, even if the session expires and JS tries to log the user out, the view still works without triggering a redirect loop.
β¨ Bonus: Understanding @login_required and @wraps
If youβre new to @login_required, itβs just a Python decorator.
def login_required(view_func):
def wrapper(request, *args, **kwargs):
if request.user.is_authenticated:
return view_func(request, *args, **kwargs)
else:
return redirect("/login/?next=" + request.path)
return wrapper
Django's version is more robust, of course. But the idea is the same.
Also, decorators often use @functools.wraps to preserve the original function's name and metadata.
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# do something
return func(*args, **kwargs)
return wrapper
β Key Takeaways
- @login_required redirects unauthenticated users to /login/?next=....
- Be careful using it on views like logout β it can cause redirect loops.
- Auto-logout features should not assume the session is always valid.
- Always test edge cases around login/logout flows and session expiry.
π¬ Final Thoughts
Sometimes a single decorator can silently cause big problems β especially when it involves session state, login redirects, or auto behaviors. If you ever face "unexpected logout" issues, check your decorators!
Feel free to share if this helped you or if you've run into similar Django surprises π§ π₯
Top comments (0)