django-session-security icon indicating copy to clipboard operation
django-session-security copied to clipboard

Post-logout redirect never fires

Open shacker opened this issue 8 years ago • 62 comments
trafficstars

I was planning to submit a PR here today, but I've hit a roadblock. Setup:

We use django_cas_ng and our users auth against a CAS SSO system. When they click the Logout link on our site, they are logged out of the site AND redirected to our campus SSO system's logout page, which kills their ticket. This is important, especially for multi-user lab computers.

After installing django-session-security, clicking the Logout link manually still works normally. But if I let a user time out with DSS, they are logged out but they are NOT redirected to the SSO logout view. They stay on the site. In this state, the user can click the Login link again and be logged in automatically again without having to authenticate (because the CAS session ticket is still alive). That's bad.

So I started a PR that lets the dev set a custom logout URL. If present, the middleware.py adds a simple redirect after logout():

        if delta >= timedelta(seconds=expire_seconds):
            logout(request)
            return HttpResponseRedirect(settings.LOGOUT_REDIRECT_URL)

(this is in process_request()). The problem is that the redirect never happens after timeout - the user is logged out but the page is not redirected to settings.LOGOUT_REDIRECT_URL. I don't understand why.

If I modify it to go to the CAS logout page without performing an internal logout first:

        if delta >= timedelta(seconds=expire_seconds):
            return HttpResponseRedirect(settings.LOGOUT_REDIRECT_URL)

Then a timeout logout does redirect, but if the user then tries to go back to the site (e.g. to log in as someone else), they're stuck in a loop eternally handing off to settings.LOGOUT_REDIRECT_URL, so they can't access the site at all.

I can't seem to make this work either way. Any idea what I'm missing here? It seems clear that No. 1 is what I want, but I can't figure out why the redirect never fires.

n.b. I also have code to call django_cas_ng's logout() function rather than Django's, but that doesn't affect the problem - it's the same either way.

shacker avatar Mar 21 '17 00:03 shacker

(this is in process_request()). The problem is that the redirect never happens after timeout - the user is logged out but the page is not redirected to settings.LOGOUT_REDIRECT_URL. I don't understand why.

Perhaps because it's an ajax request ? Maybe the fix also involves a tiny bit of JS ;)

I'm not really sure about problem 2, perhaps an example app in the test project would help ?

jpic avatar Mar 21 '17 09:03 jpic

Maybe I'm not following how this works - are you saying that this python is converted to ajax? Our logout system is not doing any ajax.

Not sure we need a full test app here - testing is as simple as adding two lines to middleware.py:

        if delta >= timedelta(seconds=expire_seconds):
            print("About to log out")
            logout(request)
            print("Just logged out")

Then let it time out. The first print statement will fire, the second will not. Or am I misunderstanding?

shacker avatar Mar 21 '17 15:03 shacker

Perhaps logout raises an exception that is later caught ?

No ajax ? Have you seen this ? https://github.com/yourlabs/django-session-security/#how-does-it-work-

jpic avatar Mar 21 '17 15:03 jpic

Ah, I wasn't clear whether you were suggesting that our SSO logout app was ajax-based or whether you meant the ajax aspects of DSS. I'm aware that DSS is doing ajax exchange, but it's not doing it at this point in the code, which is all Python, right?

So it's still not clear to me - why doesn't any python after logout() fire here? Is there another way I can handle this?

shacker avatar Mar 21 '17 19:03 shacker

Django's auth.logout() does not swallow exceptions, nor does django_cas_ng's, and we've never had a problem with logout crashing. There are no 500s in runserver console or in browser console indicating any problem there, so I don't think logout is raising an exception.

shacker avatar Mar 21 '17 19:03 shacker

K, can you use a debugger like pdb and step in logout to discover where it goes

Le 21 mars 2017 8:11 PM, "Scot Hacker" [email protected] a écrit :

Django's auth.logout() does not swallow exceptions, nor does django_cas_ng's, and we've never had a problem with logout crashing. There are no 500s in runserver console or in browser console indicating any problem there, so I don't think logout is raising an exception.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/yourlabs/django-session-security/issues/93#issuecomment-288187394, or mute the thread https://github.com/notifications/unsubscribe-auth/AAFxrE1ZIkm8NRiBcViyVTLe-YVGHTumks5roCDngaJpZM4MjMQR .

jpic avatar Mar 22 '17 08:03 jpic

OK, I've just had a very long session trying to debug this. Starting with pdb in middleware.py, then injecting pdb - and print statements - directly into Django's logout function, stepping through with n, then a much deeper dive with s, watching every step of the way and trying to figure out where it's going wrong. I just cannot figure out where it's getting derailed. No errors - It just doesn't redirect.

To simplify, I'm leaving django_cas_ng out of the picture and just dealing with django's logout function.

I then tried another approach. Realized that you're doing:

from django.contrib.auth import logout but if you use the other logout: from django.contrib.auth.views import logout

you can pass in next_page='http://example.com'

So rather than

from django.contrib.auth import logout
logout(request)
return HttpResponseRedirect('http://example.com')

I tried:

from django.contrib.auth.views import logout
logout(request, next_page="http://example.com")

No difference - same problem.

To make sure there is nothing odd going on in my somewhat complex project, I then did a fresh pip install into a new/clean Django project, and tried the same modifications to middleware.py. Same results.

This is a weird one. Quite a rabbit hole, and I'm running out of ideas.

Are you able to reproduce this?

shacker avatar Mar 22 '17 23:03 shacker

FWIW here's the tail end of a pdb session, just before and after where it claims to be doing the redirect:

> /path/to/python3.5/http/cookies.py(430)OutputString()
-> for key, value in items:
(Pdb) 
--Return--
(Pdb) > /path/to/venv/lib/python3.5/site-packages/django/contrib/staticfiles/handlers.py(63)__call__()-><HttpResponse...u/cas/logout">
-> return self.application(environ, start_response)

> /path/to/python3.5/http/cookies.py(431)OutputString()
-> if value == "":
(Pdb) 
(Pdb) > /path/to/python3.5/wsgiref/handlers.py(138)run()
-> self.finish_response()

> /path/to/python3.5/http/cookies.py(432)OutputString()
-> continue
(Pdb) 
(Pdb) [22/Mar/2017 14:33:37] "GET /session_security/ping/?idleFor=25&_=1490216704426 HTTP/1.1" 302 0
--Return--
> /path/to/python3.5/wsgiref/handlers.py(138)run()->None
-> self.finish_response()

> /path/to/python3.5/http/cookies.py(430)OutputString()
-> for key, value in items:(Pdb) 
[22/Mar/2017 14:33:38] "GET /people/ HTTP/1.1" 200 59895

shacker avatar Mar 22 '17 23:03 shacker

To be clear on my goal here: If I can successfully call django_cas_ng's logout() function, it should handle the redirect. But I can't call it, just as I can't call a redirect after Django's own logout func. The experiments above with manual redirects are because I'm trying alternative approaches.

shacker avatar Mar 23 '17 01:03 shacker

We're going to to take another approach to this and not use django-session-security, but thanks for your time.

I'm still curious about the solution to this if anyone is able to figure it out.

shacker avatar Mar 24 '17 00:03 shacker

@shacker: I have the same issue. django_cas_ng logout doesn't get fired on Ajax request on timeout. We use angular JS, django-1.9.7 and CAS for authentication. We wanted to include auto logout functionality .I have read through the comments. However, I coudn't figure-out the solution. I was wondering whether you found a solution. I have tried debugging in many ways. It works on on-click though. Can you please respond. Any suggestions are appreciated.

santosh9991 avatar Nov 16 '17 16:11 santosh9991

If you call logout from a view, you would normally return the response. As you can see here (links to an older version of Django because the interface recently changed), one of the possible responses conveys redirect information. As you saw, the middleware does not return the response. All it does is trigger the database-side processing for the logout.

But the "answer" isn't to change session security because the application cannot be responsible for timing out the SSO credential... the SSO server needs to do that. Consider the simplest case:

  • User A logs into the application
  • User A closes the browser window holding the application (there's no opportunity to redirect, even if supported)
  • User B opens a new window and goes to email

Because User A's SSO session is still alive, User B is routed to the User A's email. There's nothing that session security can do to take care of this. The SSO server needs to timeout the SSO session.

The application also needs a timeout so application sessions don't outlast the SSO sessions (by more than the timeout), but session security already handles this.

claytondaley avatar Nov 16 '17 18:11 claytondaley

@claytindaley: Thanks for taking your precious time and responding back. Thanks for above mentioned solution. I have couple of questions on the suggestions you made above: 1.Will the user be logged out abruptly after the timeout('X' amount of time) configured in SSO server. For example, if user A is logged in and he has been using the application for 10 mins. Would the User A be logged out after 10 minutes session time, abruptly even if the User A is using the application continuously?
Below is my actual issue that I am concerned about:

  1. cas_logout view is successfully called by Ajax http get request on timeout. However, user is not logged out. On page reload, user would automatically logged in. My understanding was, when ever cas_logout view is called user should be logged out and redirected to cas login page. 2.Same view when called using href(i.e on-click of sign-out button manually), it works perfectly fine. user is logged-out and redirected to cas login page My question is why does the same view behave differently, when fired through Ajax http call and on-click (through href).

santosh9991 avatar Nov 16 '17 23:11 santosh9991

Let me start by clarifying that I don't use CAS so I'm referring to SSO principles in general.

The application probably works one of two ways (determined by the plugin that manages Django auth):

  1. It authenticates once (initially) - you will remain logged into the application so long as you don't timeout within the application
  2. It reauthenticates periodically - if the SSO session ends, you will be prompted to re-authenticate at some point

However, you would expect the timer on the SSO session to be reset each time the application reauthenticates. It follows that you shouldn't be abruptly terminated by either approach (unless the SSO timeout is shorter than the application reauth time).

claytondaley avatar Nov 16 '17 23:11 claytondaley

cas_logout view is successfully called by Ajax http get request on timeout. However, user is not logged out. On page reload, user would automatically logged in

Can you screenshot the logout call that that works? The screenshot you provided shows a 403 (permission denied) error. I assume the logout wasn't successful (which would explain why the subsequent login worked). Compare the two URLs and make sure they are exactly the same.

claytondaley avatar Nov 16 '17 23:11 claytondaley

Please ignore CAS Middleware and some other print statements. I have added print statements for debugging.

santosh9991 avatar Nov 17 '17 00:11 santosh9991

If you're using Chrome's developer tools, use this option to retain a log of all of the redirects during a logout:

image

Click the manual logout button and screenshot the entry that successfully logs the user out of the SSO server. I want to confirm that you don't get a 403 on that redirect... and hopefully you can compare the URL and find a difference.

claytondaley avatar Nov 17 '17 02:11 claytondaley

I assume the two 403 calls are against the IP/domain of your SSO server, but maybe you should verify that as well.

claytondaley avatar Nov 17 '17 02:11 claytondaley

Oh I assumed that was the AJAX not the logout button. So that's what happens when you press logout... and the user is successfully logged out (of the SSO server)? Or that's what happens when you press the logout button and it doesn't work?

claytondaley avatar Nov 17 '17 20:11 claytondaley

This happens when AJAX call is made from front end after timeout(Auto time-out), Ref. Fig:4 which I had mentioned in previous comments. image Fig6: Browser logs for AJAX request. User logs out perfectly fine when logout button is pressed manualy.

santosh9991 avatar Nov 17 '17 20:11 santosh9991

User logs out perfectly fine when logout button is pressed manualy.

Yes. I'm asking you to snapshot the browser history when the user manually logs out. I want to compare those calls to the AJAX calls. I'm betting something is different.

claytondaley avatar Nov 17 '17 20:11 claytondaley

Can you scroll up and find the call to the actual SSO server? The tail end of the logs look to be the information about the current page (js libraries and such).

claytondaley avatar Nov 17 '17 21:11 claytondaley

You're looking for a call like you see in the AJAX... something like "logout/?service=..."

claytondaley avatar Nov 17 '17 21:11 claytondaley

I mean scroll up in the browser history... in the chrome developer tools window you just screenshoted.

claytondaley avatar Nov 17 '17 21:11 claytondaley

SSO logs are not saved for some reason in browser

santosh9991 avatar Nov 17 '17 21:11 santosh9991

image No logs are shown in the chrome browser when user logs out(on-click through href, Ref Fig 5 in above comments) without any issues. I see browser logs as mentioned in Fig 6. when user does not logout on AJAX call(Ref code in Fig. $)

santosh9991 avatar Nov 17 '17 21:11 santosh9991

No logs are shown in the chrome browser when user logs out(on-click through href, Ref Fig 5 in above comments) without any issues.

You should at least see the call to URLs like (obviously using your domain/IP) djangoapp.com/logout followed by ssoserver.com/login Try this sequence (leaving preserve logs on):

  • log into your application
  • click "clear" logs image
  • click logout link in app
  • (once the browser gets to the login screen) scroll to the absolute top of your chrome network logs and screenshot

You should definitely see at least the call to djangoapp.com/logout followed by some sequence that ends at ssoserver.com/login.

claytondaley avatar Nov 17 '17 21:11 claytondaley

You need to disable the filter. You see the red filter icon:

image

It's happening because you've clicked "XHR". If you unclick that icon (XHR or the filter entirely), you should get a full history.

claytondaley avatar Nov 17 '17 23:11 claytondaley

image

Fig 12: Browser logs on successful logout

santosh9991 avatar Nov 17 '17 23:11 santosh9991

That says "login". Any chance you didn't clear the logs before clicking logout?

claytondaley avatar Nov 17 '17 23:11 claytondaley