Welcome, guest | Sign In | My Account | Store | Cart

Notice! PyPM is being replaced with the ActiveState Platform, which enhances PyPM’s build and deploy capabilities. Create your free Platform account to download ActivePython or customize Python with the packages you require and get automatic updates.

Download
ActivePython
INSTALL>
pypm install simpleauth

How to install simpleauth

  1. Download and install ActivePython
  2. Open Command Prompt
  3. Type pypm install simpleauth
 Python 2.7Python 3.2Python 3.3
Windows (32-bit)
0.1.1
0.1.4Never BuiltWhy not?
0.1.2 Failed View build log
0.1.1 Available View build log
Windows (64-bit)
0.1.1
0.1.4Never BuiltWhy not?
0.1.2 Failed View build log
0.1.1 Available View build log
Mac OS X (10.5+)
0.1.1
0.1.4Never BuiltWhy not?
0.1.2 Failed View build log
0.1.1 Available View build log
Linux (32-bit)
0.1.1
0.1.4Never BuiltWhy not?
0.1.3 Failed View build log
0.1.2 Failed View build log
0.1.1 Available View build log
Linux (64-bit)
0.1.1
0.1.4 Failed View build log
0.1.3 Failed View build log
0.1.2 Failed View build log
0.1.1 Available View build log
 
License
MIT
Imports
Lastest release
version 0.1.4 on Jan 9th, 2014

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)

Demo (example app): https://simpleauth.appspot.com Source code + issues: http://code.google.com/p/gae-simpleauth/ GitHub mirror (+ more issues): https://github.com/crhym3/simpleauth

Discussions: https://groups.google.com/d/forum/gae-simpleauth

Getting Started

  1. Install the library on your local Mac/PC with one of:

System Message: WARNING/2 (<string>, line 34)

Enumerated list ends without a blank line; unexpected unindent.
  1. "easy_install -U simpleauth"
  2. "pip install simpleauth"
  3. clone the source repo, e.g. "git clone https://code.google.com/p/gae-simpleauth/"
  1. Place the subdir called "simpleauth" into your app root.

  2. You'll also need to get python-oauth2 (pip install oauth2) and httplib2 (http://code.google.com/p/httplib2/)

  3. 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

    System Message: WARNING/2 (<string>, line 58); backlink

    Inline strong start-string without end-string.

    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.

  1. 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 = [

    System Message: ERROR/3 (<string>, line 101)

    Unexpected indentation.

    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')

    System Message: WARNING/2 (<string>, line 107)

    Block quote ends without a blank line; unexpected unindent.

    ]

  2. 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 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"

Multiple callback URLs on different domains are awesomely supported. If you're running two versions of the app, say one on localhost and another on example.org, simply add all of the callbacks including host, port and protocol to Redirect URIs list on API Access tab.

== Facebook Docs: https://developers.facebook.com/docs/authentication/server-side/ 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/

Multiple callback URLs on different domains are not supported by a single app registration. If you need to test your app on say, localhost and example.org, you should probably register two different applications and use the appropriate set of key/secret: one for localhost, and the other for example.org.

Also, there's a "Sandbox Mode" checkbox on Facebook's app settings page. Make sure it's disabled for a public/live website. Otherwise, almost nobody except you will be able to authenticate.

== Windows Live Docs: http://msdn.microsoft.com/en-us/library/live/hh243649.aspx Get client/secret: https://manage.dev.live.com

Scopes are space-separated, e.g. "wl.signin wl.basic". Full list of scopes: http://msdn.microsoft.com/en-us/library/live/hh243646.aspx

Multiple callback URLs on different domains are not supported by a single app registration. If you need to test your app on say, localhost and example.org, you should probably register two different applications and use the appropriate set of key/secret: one for localhost, and the other for example.org.

== LinkedIn Docs: https://developer.linkedin.com/documents/authentication Get client/secret: https://www.linkedin.com/secure/developer

Scopes are not supported. This is OAuth 1.0a.

Even though LinkedIn will not give you any error about improper callback URI, it'll always use the value set in app's settings page. So, if you have two versions, say one on localhost and another on example.org, you'll probably want to register two applications (e.g. "dev" and "production") and use appropriate set of key/secret accordingly.

== Twitter Docs: https://dev.twitter.com/docs/auth/implementing-sign-twitter Get client/secret: https://dev.twitter.com/apps

Scopes are not supported. This is OAuth 1.0a.

When registering a new app, you have to specify a callback URL. Otherwise, it is considered as an "off-band" app and users will be given a PIN code instead of being redirected back to your site.

Even though Twitter will not give you any error about improper callback URI, it'll always use the value set in app's settings page. So, if you have two versions, say one on localhost and another on example.org, you'll probably want to register two applications (e.g. "dev" and "production") and use appropriate set of key/secret accordingly.

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):

System Message: ERROR/3 (<string>, line 218)

Unexpected indentation.
"""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:

System Message: ERROR/3 (<string>, line 225)

Unexpected indentation.
# Dispatch the request. webapp2.RequestHandler.dispatch(self)

System Message: WARNING/2 (<string>, line 227)

Block quote ends without a blank line; unexpected unindent.
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.4 - 2013-01-09
v0.1.3 - 2012-09-19

Subscribe to package updates

Last updated Jan 9th, 2014

Download Stats

Last month:1

What does the lock icon mean?

Builds marked with a lock icon are only available via PyPM to users with a current ActivePython Business Edition subscription.

Need custom builds or support?

ActivePython Enterprise Edition guarantees priority access to technical support, indemnification, expert consulting and quality-assured language builds.

Plan on re-distributing ActivePython?

Get re-distribution rights and eliminate legal risks with ActivePython OEM Edition.