Metadata-Version: 1.1
Name: simpleauth
Version: 0.1.3
Summary: A simple auth handler for Google App Engine supporting OAuth 1.0a, 2.0 and OpenID
Home-page: http://code.google.com/p/gae-simpleauth
Author: Alex Vagin (http://alex.cloudware.it)
Author-email: alex@cloudware.it
License: MIT
Download-URL: http://code.google.com/p/gae-simpleauth/source/checkout
Description: Simple authentication wrapper for an Google App Engine app
        ===========================================================
        
        Supported specs:
          - OAuth 2.0
          - OAuth 1.0(a)
          - OpenID
        
        Supported providers out of the box:
          - Google (OAuth 2.0)
          - Facebook (OAuth 2.0)
          - Windows Live (OAuth 2.0)
          - Twitter (OAuth 1.0a)
          - LinkedIn (OAuth 1.0a)
          - OpenID, using App Engine users module API
        
        Dependencies:
          - python-oauth2. This is actually a library implementing OAuth 1.0 spec.
          - httplib2 (as a dependency of python-oauth2)
          - lxml (e.g. LinkedIn user profile data parsing)
        
        Getting Started
        ================
        
        1. Install the library on your local Mac/PC with one of:
          a. "easy_install -U simpleauth"
          b. "pip install simpleauth"
          c. clone the source repo, e.g. "git clone https://code.google.com/p/gae-simpleauth/"
        
        2. Place the subdir called "simpleauth" into your app root.
        
        3. You'll also need to get python-oauth2 (pip install oauth2) 
           and httplib2 (http://code.google.com/p/httplib2/)
        
        4. Create a request handler by subclassing SimpleAuthHandler, e.g.
        
           class AuthHandler(SomeBaseRequestHandler, SimpleAuthHandler):
             """Authentication handler for all kinds of auth."""
        
             def _on_signin(self, data, auth_info, provider):
               """Callback whenever a new or existing user is logging in.
               data is a user info dictionary.
               auth_info contains access token or oauth token and secret.
               
               See what's in it with logging.info(data, auth_info)
               """
               
               auth_id = '%s:%s' % (provider, data['id'])
               
               # 1. check whether user exist, e.g.
               #    User.get_by_auth_id(auth_id)
               #
               # 2. create a new user if it doesn't
               #    User(**data).put()
               #
               # 3. sign in the user
               #    self.session['_user_id'] = auth_id
               #
               # 4. redirect somewhere, e.g. self.redirect('/profile')
               #
               # See more on how to work the above steps here:
               # http://webapp-improved.appspot.com/api/webapp2_extras/auth.html
               # http://code.google.com/p/webapp-improved/issues/detail?id=20
               
        
             def logout(self):
               self.auth.unset_session()
               self.redirect('/')
        
             def _callback_uri_for(self, provider):
               return self.uri_for('auth_callback', provider=provider, _full=True)
        
             def _get_consumer_info_for(self, provider):
               """Should return a tuple (key, secret) for auth init requests.
               For OAuth 2.0 you should also return a scope, e.g.
               ('my app id', 'my app secret', 'email,user_about_me')
               
               The scope depends solely on the provider.
               See example/secrets.py.template
               """
               return secrets.AUTH_CONFIG[provider]
        
        Note that SimpleAuthHandler isn't a real request handler. It's up to you.
        For instance, SomeBaseRequestHandler could be webapp2.RequestHandler.
        
        5. Add routing so that '/auth/PROVIDER', '/auth/PROVIDER/callback' and '/logout' requests
           go to your AuthHandler. 
           
           For instance, in webapp2 you could do:
           
           # Map URLs to handlers
           routes = [
             Route('/auth/<provider>',
               handler='handlers.AuthHandler:_simple_auth', name='auth_login'),
             Route('/auth/<provider>/callback', 
               handler='handlers.AuthHandler:_auth_callback', name='auth_callback'),
             Route('/logout',
               handler='handlers.AuthHandler:logout', name='logout')
           ]
        
        6. That's it. See a sample app in the example dir. 
           To run the example app, copy example/secrets.py.template into example/secrets.py
           and start the app locally by executing run.sh
        
        OAuth scopes, keys and secrets
        ===============================
        
        This section is just a bunch of links to the docs on authentication with
        various providers.
        
        == Google
        Docs:
        https://developers.google.com/accounts/docs/OAuth2WebServer
        
        Where to get client/secret:
        http://code.google.com/apis/console
        
        Multiple scopes should be space-separated, e.g.
        "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email"
        
        == Facebook
        Docs:
        https://developers.facebook.com/docs/authentication/server-side/
        
        Where to get client/secret:
        https://developers.facebook.com/apps
        
        Multiple Scopes should be comma-separated, e.g. "user_about_me,email".
        Full list of scopes:
        http://developers.facebook.com/docs/authentication/permissions/
        
        == Windows Live
        Docs:
        http://msdn.microsoft.com/en-us/library/live/hh243649.aspx
        
        Where to get client/secret:
        https://manage.dev.live.com/AddApplication.aspx
        
        Scopes are space-separated, e.g. "wl.signin wl.basic".
        Full list of scopes:
        http://msdn.microsoft.com/en-us/library/live/hh243646.aspx
        
        CSRF protection
        ================
        
        You can optionally enable cross-site-request-forgery protection for OAuth 2.0:
        
           class AuthHandler(webapp2.RequestHandler, SimpleAuthHandler):
        
             # enabled CSRF state token for OAuth 2.0
             OAUTH2_CSRF_STATE = True
        
             # ...
             # rest of the stuff from step 4 of the above.
        
        This will use the optional OAuth 2.0 'state' param to guard against CSRFs by
        setting a user session token during Authorization step and comparing it 
        against 'state' parameter on callback.
        
        For this to work your handler has to have a session dict-like object on the 
        instance. Here's an example using webapp2_extras session:
        
           import webapp2
           from webapp2_extras import sessions
        
           class AuthHandler(webapp2.RequestHandler, SimpleAuthHandler):
             # enabled CSRF state token for OAuth 2.0
             OAUTH2_CSRF_STATE = True
        
             @webapp2.cached_property
             def session(self):
               """Returns a session using the default cookie key"""
               return self.session_store.get_session()
        
             def dispatch(self):
               # Get a session store for this request.
               self.session_store = sessions.get_store(request=self.request)
               try:
                 # Dispatch the request.
                 webapp2.RequestHandler.dispatch(self)
               finally:
                 # Save all sessions.
                 self.session_store.save_sessions(self.response)
        
             # ...
             # rest of the stuff from step 4 of the above.
        
        This simple implementation assumes it is safe to use user sessions.
        If, however, user's session can be hijacked, the authentication flow could
        probably be bypassed anyway and this CSRF protection becomes the least 
        of the problems.
        
        Alternative implementation could involve HMAC digest. If anything serious 
        pops up (e.g. see this SO question: http://goo.gl/3hiOv) please submit 
        a bug on the issue tracker.
        
        Catching errors
        ================
        
        There are a couple ways to catch authentication errors if you don't want your
        app to display a "Server Error" message when something goes wrong during
        an auth flow.
        
        You can use webapp2's built-in functionality and define
        handle_exception(self, exception, debug) instance method on the handler
        that processes authentication requests or on a base handler if you have one.
        Here's a simple example:
        
           class AuthHandler(webapp2.RequestHandler, SimpleAuthHandler):
             # _on_signin() and other stuff
             # ...
        
             def handle_exception(self, exception, debug):
               # Log the error
               logging.error(exception)
               # Do something based on the exception: notify users, etc.
               self.response.write(exception)
               self.response.set_status(500)
        
        You can also define global (app-wise) error handlers using app.error_handlers
        dict (where app is a webapp2.WSGIApplication instance). It's all described
        in details on this page:
        http://webapp-improved.appspot.com/guide/exceptions.html
        
        Another solution is, if you're using webapp2's dispatch method like in the
        CSRF snippet above, you could do something like this:
        
           from simpleauth import Error as AuthError
        
           def dispatch(self):
             try:
               # Dispatch the request.
               webapp2.RequestHandler.dispatch(self)
             except AuthError as e:
               # Do something based on the error: notify users, etc.
               logging.error(e)
               self.redirect('/')
        
        Alternatively, you can also use App Engine built-in functionality and define
        error handlers in app.yaml. Docs on this can be found here:
        https://developers.google.com/appengine/docs/python/config/appconfig
        (see Custom Error Responses section).
        
        Lastly, if nothing from the above works for you, override _simple_auth()
        and/or _auth_callback() methods, e.g.
        
           from simpleauth import SimpleAuthHandler
           from simpleauth import Error as AuthError
        
           class AuthHandler(webapp2.RequestHandler, SimpleAuthHandler):
             def _simple_auth(self, provider=None):
               try:
                 super(AuthHandler, self)._simple_auth(provider)
               except AuthError as e:
                 # Do something based on the error: notify users, etc.
                 logging.error(e)
                 self.redirect('/')
        
        CHANGELOG
        ================
        
        v0.1.3 - 2012-09-19
          * CSRF protection for OAuth 2.0
            http://code.google.com/p/gae-simpleauth/issues/detail?id=1
          * Custom exceptions
            http://code.google.com/p/gae-simpleauth/issues/detail?id=2
          * Example app improvements, including:
            - CSRF guard
            - show exception messages for demo purposes
            - prettier output of session, profile data and auth_info dictionaries
            - https://github.com/crhym3/simpleauth/issues/4
            - https://github.com/crhym3/simpleauth/issues/5
          * More useful info in README
        
Keywords: oauth oauth2 openid appengine google
Platform: any
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2.7
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Libraries
Requires: lxml
Requires: oauth2
Requires: httplib2
