Logout redirecting to logout url again

This is a very strange behaviour I am seeing. After clicking the logout button Mozilla is sometimes trying to load the “/logout” url, however as in the first click the user gets logged out and because of that it is again redirecting to login url with a “next” parameter.

    @login_required(login_url='login')
    def logout(request):
        try:
            del(request.session['latitude'])
            del(request.session['longitude'])
            del(request.session['current_url'])
        except KeyError:
            pass
        auth.logout(request)
        messages.info(request, "You have been successfully logged out")
        return redirect('login')

This is logout code. And here are the logs from gunicorn :


    Mar 24 03:42:39 PUCHI gunicorn[18959]:  - - [24/Mar/2024:03:42:39 +0530] "POST /accounts/login/ HTTP/1.0" 302 0 "http://127.0.0.1/accounts/login/?next=/accounts/logout/" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:123.0) Gecko/20100101 Firefox/123.0"
    Mar 24 03:42:39 PUCHI gunicorn[18959]:  - - [24/Mar/2024:03:42:39 +0530] "GET /accounts/myAccount/ HTTP/1.0" 302 0 "http://127.0.0.1/accounts/login/?next=/accounts/logout/" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:123.0) Gecko/20100101 Firefox/123.0"
    Mar 24 03:42:39 PUCHI gunicorn[18959]:  - - [24/Mar/2024:03:42:39 +0530] "GET /accounts/customer/Dashboard/ HTTP/1.0" 200 11755 "http://127.0.0.1/accounts/login/?next=/accounts/logout/" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:123.0) Gecko/20100101 Firefox/123.0"
        
    Mar 24 03:42:41 PUCHI gunicorn[18956]:  - - [24/Mar/2024:03:42:41 +0530] "GET /accounts/logout/ HTTP/1.0" 302 0 "http://127.0.0.1/accounts/customer/Dashboard/" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:123.0) Gecko/20100101 Firefox/123.0"
    Mar 24 03:42:41 PUCHI gunicorn[18959]:  - - [24/Mar/2024:03:42:41 +0530] "GET /accounts/logout/ HTTP/1.0" 302 0 "http://127.0.0.1/accounts/customer/Dashboard/" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:123.0) Gecko/20100101 Firefox/123.0"
       
    Mar 24 03:42:41 PUCHI gunicorn[18959]:  - - [24/Mar/2024:03:42:41 +0530] "GET /accounts/login/?next=/accounts/logout/ HTTP/1.0" 200 10760 "http://127.0.0.1/accounts/customer/Dashboard/" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:123.0) Gecko/20100101 Firefox/123.0"

As you can see in the middle section logout is redirecting to logout again. And it is worth mentioning that this happens only when I use nginx and gunicorn on my localserver.
Can anyone help on this please?

Side note: When fencing off blocks of code (or error logs), you need to use the backtick - ` character, not the apostrophy - '. (I’ve taken the liberty of fixing this for you.)

Side note 2: You don’t need to delete the session data manually.
From the docs How to log a user out:

When you call logout(), the session data for the current request is completely cleaned out. All existing data is removed.

What are your LOGIN_URL, LOGIN_REDIRECT_URL and LOGOUT_REDIRECT_URL settings?

What is the page you are on that is directing you to this view?

What do your url definitions look like for your login and logout processes?

I have only set the LOGOUT_REDIRECT_URL = ‘accounts/login’ in the settings. But not others. And here is the login view for the url.

def login(request):
    if request.user.is_authenticated:
        return logged_in_redirect(request, "You are already logged In !")

    if request.method == 'POST':
        username = request.POST['username']
        password = request.POST['password']

        user = auth.authenticate(username=username, password=password)
        if user:
            auth.login(request, user)
            messages.success(request, 'You are now Logged In !!!')
            return redirect('my_account')

        else:
            messages.error(request, 'Invalid Credentials')
            return redirect('login')
    else:
        form = UserForm()
        context = {
            'form': form,
        }
        return render(request, 'accounts/login.html', context)

After logging in the view redirects me to another url named ‘my_account’ which decides what type of user is logging in and redirects them to appropriate dashboard. Here is the my_account view:

@login_required(login_url='login')
def my_account(request):
    role = request.user.get_role()
    if role == 'vendor':
        return redirect('vendor_dashboard')
    elif role == 'customer':
        return redirect('customer_dashboard')
    elif role == 'super_admin':
        return redirect('/admin')
    else:
        return redirect('home')

And just after logging in as a customer when I logout from the ‘customer_dashboard’ url it tries to again go to the logout url, which should instead be the login url.

What is the URL that has been rendered for the logout menu entry on your customer_dashboard page?

Also, I’m not sure I’m following what you’re saying here. The more I look at the log (realizing there is a request from a difference session from the rest in it), the more I think it looks right to me. I’m not seeing a problem with what you’ve posted.

Ok let me explain my doubt very clearly. I have cleared all the logs and starting fresh.

The Basic functionality is after someone enters valid credentials in the login url it sends a POST request to the same url (code below) and after succesfully logging the user in, it redirects to another url called ‘myAccount’ which decides whether the user is a vendor or a customer or an admin and redirects him to appropriate dashboard.

Here are the steps I am doing in my browser:

  1. I got to the login url.

  2. Logged in as a customer.

  3. It landed me on the customer dashboard page.

  4. Then I logged out.

  5. And the same problem again it redirects me to a url with next parameter for logging in.

Here are the logs:

    Mar 24 14:49:26 PUCHI gunicorn[5315]:  - - [24/Mar/2024:14:49:26 +0530] "GET /accounts/login/ HTTP/1.0" 200 10760 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:123.0) Gecko/20100101 Firefox/123.0"
    Mar 24 14:50:22 PUCHI gunicorn[5314]:  - - [24/Mar/2024:14:50:22 +0530] "POST /accounts/login/ HTTP/1.0" 302 0 "http://127.0.0.1/accounts/login/" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:123.0) Gecko/20100101 Firefox/123.0"
    Mar 24 14:50:22 PUCHI gunicorn[5314]:  - - [24/Mar/2024:14:50:22 +0530] "GET /accounts/myAccount/ HTTP/1.0" 302 0 "http://127.0.0.1/accounts/login/" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:123.0) Gecko/20100101 Firefox/123.0"
    Mar 24 14:50:22 PUCHI gunicorn[5314]:  - - [24/Mar/2024:14:50:22 +0530] "GET /accounts/customer/Dashboard/ HTTP/1.0" 200 11791 "http://127.0.0.1/accounts/login/" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:123.0) Gecko/20100101 Firefox/123.0"
    Mar 24 14:51:02 PUCHI gunicorn[5313]:  - - [24/Mar/2024:14:51:02 +0530] "GET /accounts/logout/ HTTP/1.0" 302 0 "http://127.0.0.1/accounts/customer/Dashboard/" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:123.0) Gecko/20100101 Firefox/123.0"
    Mar 24 14:51:02 PUCHI gunicorn[5315]:  - - [24/Mar/2024:14:51:02 +0530] "GET /accounts/logout/ HTTP/1.0" 302 0 "http://127.0.0.1/accounts/customer/Dashboard/" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:123.0) Gecko/20100101 Firefox/123.0"
    Mar 24 14:51:02 PUCHI gunicorn[5315]:  - - [24/Mar/2024:14:51:02 +0530] "GET /accounts/login/?next=/accounts/logout/ HTTP/1.0" 200 10760 "http://127.0.0.1/accounts/customer/Dashboard/" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:123.0) Gecko/20100101 Firefox/123.0"

As per the logs:

  1. I got to the login url.
  2. Then entered the credentials of a customer which sent a POST request to the same url
  3. Then it redirected me to ‘myAccount’ url to decide what kind of user has logged in
  4. After determining the user as a customer it redirected me to customer dashboard url

In the customer dashboard page I have an <a> tag with href attribute set to the logout url. When I attempted to logout through the link it redirected me to the logout url. But here is where the unexpected thing happened.

As per the logs after sending the GET request to the logout url it tried to send again a GET request to the logout url. However in the logout view I have redirected the user to the login url. Then why is it trying to go to the logout url again?

Also when it hit the logout url first time, the user got logged out. And because I have a ‘login_required’ decorator set for the logout url, in the second request when the user is no more logged in, it is redirecting me to the login url with the next parameter.

Here I are the relevant views:

    def login(request):
        if request.user.is_authenticated:
            return logged_in_redirect(request, "You are already logged In !")
     
        if request.method == 'POST':
            username = request.POST['username']
            password = request.POST['password']
     
            user = auth.authenticate(username=username, password=password)
            if user:
                auth.login(request, user)
                messages.success(request, 'You are now Logged In !!!')
                return redirect('my_account')
     
            else:
                messages.error(request, 'Invalid Credentials')
                return redirect('login')
        else:
            form = UserForm()
            context = {
                'form': form,
            }
            return render(request, 'accounts/login.html', context)
     
     
    @login_required(login_url='login')
    def logout(request):
        auth.logout(request)
        messages.info(request, "You have been successfully logged out")
        return redirect('login')
     
     
    @login_required(login_url='login')
    def my_account(request):
        role = request.user.get_role()
        if role == 'vendor':
            return redirect('vendor_dashboard')
        elif role == 'customer':
            return redirect('customer_dashboard')
        elif role == 'super_admin':
            return redirect('/admin')
        else:
            return redirect('home')

It looks like redirect('login') in the logout view doesn’t redirect to login view but to logout view. Then on the subsequent call to logout, because you have the @login_required decorator and the user in not logged in anymore, then the decorator redirects you to the login_url with the next parameter set so that you will be redirected to the page expecteing a logged in user (here the logout page) after logging in : this is the normal behaviour of the @login_required decorator.

First, I would remove the @login_required decorator from the logout view because it is useless. Also, that will avoid the round trip between logout → login → logout when a not-logged-in user explicitely go to the logout url in her/his browser.

Then, try to understand why logout is redirecting to logout itself instead of login: is the redirect("login") really this in your code or is there an error so that it uses redirect("logout") instead. What is the definition of urlpatterns for those views in your urls.py file ? Does the path for logout view is correctly named, or is there a mistake so that it is named login ?

Just thought about another reason why you may see 2 calls to the logout url:

If double clicking the logout link, you will see those two calls : the first one logs out user and returns the redirect to login but it is not taken into account by the browser because you clicked again on the link in the meanwhile. This second call then fall in the @login_required behaviour of redirecting the unlogged user to login page with the next parameter.

If not double clicking the link, there may be something making this double call (some javascript, …)

By the way, by removing the @login_required decorator from the logout view, you should always be redirected to login view without next parameter after hitting the logout view.

Also, you may consider using POST instead of GET for logout view, as this is considered best practice

Yes now I found out that a javascript function is making the second call which I used to clear the session storage. But I can do it asynchronously. This was a really terrible bug to fix.

But thank you so much. It’s now fixed.