django-session-security
django-session-security copied to clipboard
Post-logout redirect never fires
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.
(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 ?
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?
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-
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?
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.
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 .
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?
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
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.
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: 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.
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.
@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:
- 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).
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):
- It authenticates once (initially) - you will remain logged into the application so long as you don't timeout within the application
- 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).
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.
Please ignore CAS Middleware and some other print statements. I have added print statements for debugging.
If you're using Chrome's developer tools, use this option to retain a log of all of the redirects during a logout:

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.
I assume the two 403 calls are against the IP/domain of your SSO server, but maybe you should verify that as well.
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?
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.
Fig6: Browser logs for AJAX request.
User logs out perfectly fine when logout button is pressed manualy.
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.
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).
You're looking for a call like you see in the AJAX... something like "logout/?service=..."
I mean scroll up in the browser history... in the chrome developer tools window you just screenshoted.
SSO logs are not saved for some reason in browser
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. $)
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

- 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.
You need to disable the filter. You see the red filter icon:

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

Fig 12: Browser logs on successful logout
That says "login". Any chance you didn't clear the logs before clicking logout?