2011-12-18

How to log out from an AppEngine app only

This blog post explains how to add a logout page to an AppEngine Python application, which will log out of the application only, without logging out of all of Google (e.g. Gmail, Calendar, Picasa, YouTube).

The default AppEngine users.create_logout_url(...) crates a logout URL which would log out from all of Google. To log out from the AppEngine app only, remove the SACSID and ACSID session cookies, which AppEngine has set right after logging in. Here is how to do it in Python:

import Cookie
import os
from google.appengine.api import users
from google.appengine.ext import webapp

class LogoutPage(webapp.RequestHandler):
  def get(self):
    target_url = self.request.referer or '/'
    if os.environ.get('SERVER_SOFTWARE', '').startswith('Development/'):
      self.redirect(users.create_logout_url(target_url))
      return

    # On the production instance, we just remove the session cookie, because
    # redirecting users.create_logout_url(...) would log out of all Google
    # (e.g. Gmail, Google Calendar).
    #
    # It seems that AppEngine is setting the ACSID cookie for http:// ,
    # and the SACSID cookie for https:// . We just unset both below.
    cookie = Cookie.SimpleCookie()
    cookie['ACSID'] = ''
    cookie['ACSID']['expires'] = -86400  # In the past, a day ago.
    self.response.headers.add_header(*cookie.output().split(': ', 1))
    cookie = Cookie.SimpleCookie()
    cookie['SACSID'] = ''
    cookie['SACSID']['expires'] = -86400
    self.response.headers.add_header(*cookie.output().split(': ', 1))
    self.redirect(target_url) 

...

application = webapp.WSGIApplication([..., ('/logout', LogoutPage), ...])
def main():
  run_wsgi_app(application)
if __name__ == '__main__':
  main()

After adding the /logout page as indicated above, offer the logout link like this:

self.response.out.write('<a href="/logout">Logout</a>')

Please note that adding a cookie which expires in the past makes the browser forget about the cookie immediately.

5 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. I'm not entirely clear on the circumstances, but I think depending on your route configuration, the cookie may get applied to the incorrect path. The issue I ran into was that the cookie was only being set for my '/logout' url. To fix this, set '/' to the path parameter.

    cookie['ACSID']['path'] = '/'
    cookie['SACSID']['path'] = '/'

    ReplyDelete
  3. @Sherpa: It worked for me without specifying the 'path' explicitly. But thanks for posting anyway, someone might run into the same problem.

    ReplyDelete
  4. Right. It was a really confusing bug because it worked fine on two of our other sites. The main difference with the new site is that we're using a heavily modified app engine boilerplate, which uses webapp2's session manager and RedirectRoutes. I haven't taken the time to figure out, but my guess is that it's related to that.

    ReplyDelete