# License for the specific language governing permissions and limitations
# under the License.
-"""Implementations of various third-party authentication schemes.
+"""This module contains implementations of various third-party
+authentication schemes.
-All the classes in this file are class Mixins designed to be used with
-web.py RequestHandler classes. The primary methods for each service are
-authenticate_redirect(), authorize_redirect(), and get_authenticated_user().
-The former should be called to redirect the user to, e.g., the OpenID
-authentication page on the third party service, and the latter should
-be called upon return to get the user data from the data returned by
-the third party service.
+All the classes in this file are class mixins designed to be used with
+the `tornado.web.RequestHandler` class. They are used in two ways:
+
+* On a login handler, use methods such as ``authenticate_redirect()``,
+ ``authorize_redirect()``, and ``get_authenticated_user()`` to
+ establish the user's identity and store authentication tokens to your
+ database and/or cookies.
+* In non-login handlers, use methods such as ``facebook_request()``
+ or ``twitter_request()`` to use the authentication tokens to make
+ requests to the respective services.
They all take slightly different arguments due to the fact all these
services implement authentication and authorization slightly differently.
class OpenIdMixin(object):
"""Abstract implementation of OpenID and Attribute Exchange.
- See GoogleMixin below for example implementations.
+ See `GoogleMixin` below for a customized example (which also
+ includes OAuth support).
+
+ Class attributes:
+
+ * ``_OPENID_ENDPOINT``: the identity provider's URI.
"""
def authenticate_redirect(self, callback_uri=None,
ax_attrs=["name", "email", "language", "username"]):
- """Returns the authentication URL for this service.
+ """Redirects to the authentication URL for this service.
After authentication, the service will redirect back to the given
- callback URI.
+ callback URI with additional parameters including ``openid.mode``.
We request the given attributes for the authenticated user by
default (name, email, language, and username). If you don't need
"""Fetches the authenticated user data upon redirect.
This method should be called by the handler that receives the
- redirect from the authenticate_redirect() or authorize_redirect()
- methods.
+ redirect from the `authenticate_redirect()` method (which is
+ often the same as the one that calls it; in that case you would
+ call `get_authenticated_user` if the ``openid.mode`` parameter
+ is present and `authenticate_redirect` if it is not).
+
+ The result of this method will generally be used to set a cookie.
"""
# Verify the OpenID response via direct request to the OP
args = dict((k, v[-1]) for k, v in self.request.arguments.items())
future.set_result(user)
def get_auth_http_client(self):
- """Returns the AsyncHTTPClient instance to be used for auth requests.
+ """Returns the `AsyncHTTPClient` instance to be used for auth requests.
- May be overridden by subclasses to use an http client other than
+ May be overridden by subclasses to use an HTTP client other than
the default.
"""
return httpclient.AsyncHTTPClient()
class OAuthMixin(object):
- """Abstract implementation of OAuth.
+ """Abstract implementation of OAuth 1.0 and 1.0a.
+
+ See `TwitterMixin` and `FriendFeedMixin` below for example implementations,
+ or `GoogleMixin` for an OAuth/OpenID hybrid.
+
+ Class attributes:
+
+ * ``_OAUTH_AUTHORIZE_URL``: The service's OAuth authorization url.
+ * ``_OAUTH_ACCESS_TOKEN_URL``: The service's OAuth access token url.
+ * ``_OAUTH_VERSION``: May be either "1.0" or "1.0a".
+ * ``_OAUTH_NO_CALLBACKS``: Set this to True if the service requires
+ advance registration of callbacks.
- See TwitterMixin and FriendFeedMixin below for example implementations.
+ Subclasses must also override the `_oauth_get_user_future` and
+ `_oauth_consumer_token` methods.
"""
def authorize_redirect(self, callback_uri=None, extra_params=None,
http_client=None):
"""Redirects the user to obtain OAuth authorization for this service.
- Twitter and FriendFeed both require that you register a Callback
- URL with your application. You should call this method to log the
- user in, and then call get_authenticated_user() in the handler
- you registered as your Callback URL to complete the authorization
- process.
+ The ``callback_uri`` may be omitted if you have previously
+ registered a callback URI with the third-party service. For some
+ sevices (including Twitter and Friendfeed), you must use a
+ previously-registered callback URI and cannot specify a callback
+ via this method.
- This method sets a cookie called _oauth_request_token which is
- subsequently used (and cleared) in get_authenticated_user for
+ This method sets a cookie called ``_oauth_request_token`` which is
+ subsequently used (and cleared) in `get_authenticated_user` for
security purposes.
"""
if callback_uri and getattr(self, "_OAUTH_NO_CALLBACKS", False):
@_auth_return_future
def get_authenticated_user(self, callback, http_client=None):
- """Gets the OAuth authorized user and access token on callback.
-
- This method should be called from the handler for your registered
- OAuth Callback URL to complete the registration process. We call
- callback with the authenticated user, which in addition to standard
- attributes like 'name' includes the 'access_key' attribute, which
- contains the OAuth access you can use to make authorized requests
- to this service on behalf of the user.
-
+ """Gets the OAuth authorized user and access token.
+
+ This method should be called from the handler for your
+ OAuth callback URL to complete the registration process. We run the
+ callback with the authenticated user dictionary. This dictionary
+ will contain an ``access_key`` which can be used to make authorized
+ requests to this service on behalf of the user. The dictionary will
+ also contain other fields such as ``name``, depending on the service
+ used.
"""
future = callback
request_key = escape.utf8(self.get_argument("oauth_token"))
self._oauth_get_user_future(access_token).add_done_callback(
self.async_callback(self._on_oauth_get_user, access_token, future))
+ def _oauth_consumer_token(self):
+ """Subclasses must override this to return their OAuth consumer keys.
+
+ The return value should be a `dict` with keys ``key`` and ``secret``.
+ """
+ raise NotImplementedError()
+
@return_future
def _oauth_get_user_future(self, access_token, callback):
+ """Subclasses must override this to get basic information about the
+ user.
+
+ Should return a `Future` whose result is a dictionary containing
+ information about the user, which may have been retrieved by
+ using ``access_token`` to make a request to the service.
+
+ The access token will be added to the returned dictionary to make
+ the result of `get_authenticated_user`.
+
+ For backwards compatibility, the callback-based `_oauth_get_user`
+ method is also supported.
+ """
# By default, call the old-style _oauth_get_user, but new code
# should override this method instead.
self._oauth_get_user(access_token, callback)
return base_args
def get_auth_http_client(self):
- """Returns the AsyncHTTPClient instance to be used for auth requests.
+ """Returns the `AsyncHTTPClient` instance to be used for auth requests.
- May be overridden by subclasses to use an http client other than
+ May be overridden by subclasses to use an HTTP client other than
the default.
"""
return httpclient.AsyncHTTPClient()
class OAuth2Mixin(object):
- """Abstract implementation of OAuth v 2."""
+ """Abstract implementation of OAuth 2.0.
+
+ See `FacebookGraphMixin` below for an example implementation.
+
+ Class attributes:
+
+ * ``_OAUTH_AUTHORIZE_URL``: The service's authorization url.
+ * ``_OAUTH_ACCESS_TOKEN_URL``: The service's access token url.
+ """
def authorize_redirect(self, redirect_uri=None, client_id=None,
client_secret=None, extra_params=None):
"""Redirects the user to obtain OAuth authorization for this service.
- Some providers require that you register a Callback
- URL with your application. You should call this method to log the
- user in, and then call get_authenticated_user() in the handler
- you registered as your Callback URL to complete the authorization
- process.
+ Some providers require that you register a redirect URL with
+ your application instead of passing one via this method. You
+ should call this method to log the user in, and then call
+ `get_authenticated_user()` in the handler for your
+ redirect URL to complete the authorization process.
"""
args = {
"redirect_uri": redirect_uri,
"""Twitter OAuth authentication.
To authenticate with Twitter, register your application with
- Twitter at http://twitter.com/apps. Then copy your Consumer Key and
- Consumer Secret to the application settings 'twitter_consumer_key' and
- 'twitter_consumer_secret'. Use this Mixin on the handler for the URL
- you registered as your application's Callback URL.
+ Twitter at http://twitter.com/apps. Then copy your Consumer Key
+ and Consumer Secret to the application
+ `~tornado.web.Application.settings` ``twitter_consumer_key`` and
+ ``twitter_consumer_secret``. Use this mixin on the handler for the
+ URL you registered as your application's callback URL.
- When your application is set up, you can use this Mixin like this
+ When your application is set up, you can use this mixin like this
to authenticate the user with Twitter and get access to their stream::
- class TwitterHandler(tornado.web.RequestHandler,
- tornado.auth.TwitterMixin):
+ class TwitterLoginHandler(tornado.web.RequestHandler,
+ tornado.auth.TwitterMixin):
@tornado.web.asynchronous
def get(self):
if self.get_argument("oauth_token", None):
raise tornado.web.HTTPError(500, "Twitter auth failed")
# Save the user using, e.g., set_secure_cookie()
- The user object returned by get_authenticated_user() includes the
- attributes 'username', 'name', and all of the custom Twitter user
- attributes describe at
- http://apiwiki.twitter.com/Twitter-REST-API-Method%3A-users%C2%A0show
- in addition to 'access_token'. You should save the access token with
- the user; it is required to make requests on behalf of the user later
- with twitter_request().
+ The user object returned by `get_authenticated_user()` includes the
+ attributes ``username``, ``name``, ``access_token``, and all of the
+ custom Twitter user attributes described at
+ https://dev.twitter.com/docs/api/1.1/get/users/show
"""
_OAUTH_REQUEST_TOKEN_URL = "http://api.twitter.com/oauth/request_token"
_OAUTH_ACCESS_TOKEN_URL = "http://api.twitter.com/oauth/access_token"
_TWITTER_BASE_URL = "http://api.twitter.com/1"
def authenticate_redirect(self, callback_uri=None):
- """Just like authorize_redirect(), but auto-redirects if authorized.
+ """Just like `~OAuthMixin.authorize_redirect`, but
+ auto-redirects if authorized.
This is generally the right interface to use if you are using
Twitter for single-sign on.
@_auth_return_future
def twitter_request(self, path, callback=None, access_token=None,
post_args=None, **args):
- """Fetches the given API path, e.g., "/statuses/user_timeline/btaylor"
+ """Fetches the given API path, e.g., ``/statuses/user_timeline/btaylor``
- The path should not include the format (we automatically append
- ".json" and parse the JSON output).
+ The path should not include the format or API version number.
+ (we automatically use JSON format and API version 1).
- If the request is a POST, post_args should be provided. Query
+ If the request is a POST, ``post_args`` should be provided. Query
string arguments should be given as keyword arguments.
- All the Twitter methods are documented at
- http://apiwiki.twitter.com/Twitter-API-Documentation.
+ All the Twitter methods are documented at http://dev.twitter.com/
- Many methods require an OAuth access token which you can obtain
- through authorize_redirect() and get_authenticated_user(). The
- user returned through that process includes an 'access_token'
- attribute that can be used to make authenticated requests via
- this method. Example usage::
+ Many methods require an OAuth access token which you can
+ obtain through `~OAuthMixin.authorize_redirect` and
+ `~OAuthMixin.get_authenticated_user`. The user returned through that
+ process includes an 'access_token' attribute that can be used
+ to make authenticated requests via this method. Example
+ usage::
class MainHandler(tornado.web.RequestHandler,
tornado.auth.TwitterMixin):
"""FriendFeed OAuth authentication.
To authenticate with FriendFeed, register your application with
- FriendFeed at http://friendfeed.com/api/applications. Then
- copy your Consumer Key and Consumer Secret to the application settings
- 'friendfeed_consumer_key' and 'friendfeed_consumer_secret'. Use
- this Mixin on the handler for the URL you registered as your
- application's Callback URL.
+ FriendFeed at http://friendfeed.com/api/applications. Then copy
+ your Consumer Key and Consumer Secret to the application
+ `~tornado.web.Application.settings` ``friendfeed_consumer_key``
+ and ``friendfeed_consumer_secret``. Use this mixin on the handler
+ for the URL you registered as your application's Callback URL.
- When your application is set up, you can use this Mixin like this
+ When your application is set up, you can use this mixin like this
to authenticate the user with FriendFeed and get access to their feed::
class FriendFeedHandler(tornado.web.RequestHandler,
raise tornado.web.HTTPError(500, "FriendFeed auth failed")
# Save the user using, e.g., set_secure_cookie()
- The user object returned by get_authenticated_user() includes the
- attributes 'username', 'name', and 'description' in addition to
- 'access_token'. You should save the access token with the user;
+ The user object returned by `~OAuthMixin.get_authenticated_user()` includes the
+ attributes ``username``, ``name``, and ``description`` in addition to
+ ``access_token``. You should save the access token with the user;
it is required to make requests on behalf of the user later with
- friendfeed_request().
+ `friendfeed_request()`.
"""
_OAUTH_VERSION = "1.0"
_OAUTH_REQUEST_TOKEN_URL = "https://friendfeed.com/account/oauth/request_token"
post_args=None, **args):
"""Fetches the given relative API path, e.g., "/bret/friends"
- If the request is a POST, post_args should be provided. Query
+ If the request is a POST, ``post_args`` should be provided. Query
string arguments should be given as keyword arguments.
All the FriendFeed methods are documented at
http://friendfeed.com/api/documentation.
- Many methods require an OAuth access token which you can obtain
- through authorize_redirect() and get_authenticated_user(). The
- user returned through that process includes an 'access_token'
- attribute that can be used to make authenticated requests via
- this method. Example usage::
+ Many methods require an OAuth access token which you can
+ obtain through `~OAuthMixin.authorize_redirect` and
+ `~OAuthMixin.get_authenticated_user`. The user returned
+ through that process includes an ``access_token`` attribute that
+ can be used to make authenticated requests via this
+ method.
+
+ Example usage::
class MainHandler(tornado.web.RequestHandler,
tornado.auth.FriendFeedMixin):
class GoogleMixin(OpenIdMixin, OAuthMixin):
"""Google Open ID / OAuth authentication.
- No application registration is necessary to use Google for authentication
- or to access Google resources on behalf of a user. To authenticate with
- Google, redirect with authenticate_redirect(). On return, parse the
- response with get_authenticated_user(). We send a dict containing the
- values for the user, including 'email', 'name', and 'locale'.
+ No application registration is necessary to use Google for
+ authentication or to access Google resources on behalf of a user.
+
+ Google implements both OpenID and OAuth in a hybrid mode. If you
+ just need the user's identity, use
+ `~OpenIDMixin.authenticate_redirect`. If you need to make
+ requests to Google on behalf of the user, use
+ `authorize_redirect`. On return, parse the response with
+ `~OpenIDMixin.get_authenticated_user()`. We send a dict containing
+ the values for the user, including ``email``, ``name``, and
+ ``locale``.
+
Example usage::
class GoogleHandler(tornado.web.RequestHandler, tornado.auth.GoogleMixin):
ax_attrs=["name", "email", "language", "username"]):
"""Authenticates and authorizes for the given Google resource.
- Some of the available resources are:
+ Some of the available resources which can be used in the ``oauth_scope``
+ argument are:
* Gmail Contacts - http://www.google.com/m8/feeds/
* Calendar - http://www.google.com/calendar/feeds/
To authenticate with Facebook, register your application with
Facebook at http://www.facebook.com/developers/apps.php. Then
copy your API Key and Application Secret to the application settings
- 'facebook_api_key' and 'facebook_secret'.
+ ``facebook_api_key`` and ``facebook_secret``.
- When your application is set up, you can use this Mixin like this
+ When your application is set up, you can use this mixin like this
to authenticate the user with Facebook::
class FacebookHandler(tornado.web.RequestHandler,
raise tornado.web.HTTPError(500, "Facebook auth failed")
# Save the user using, e.g., set_secure_cookie()
- The user object returned by get_authenticated_user() includes the
- attributes 'facebook_uid' and 'name' in addition to session attributes
- like 'session_key'. You should save the session key with the user; it is
+ The user object returned by `get_authenticated_user` includes the
+ attributes ``facebook_uid`` and ``name`` in addition to session attributes
+ like ``session_key``. You should save the session key with the user; it is
required to make requests on behalf of the user later with
- facebook_request().
+ `facebook_request`.
"""
def authenticate_redirect(self, callback_uri=None, cancel_uri=None,
extended_permissions=None):
return hashlib.md5(body).hexdigest()
def get_auth_http_client(self):
- """Returns the AsyncHTTPClient instance to be used for auth requests.
+ """Returns the `AsyncHTTPClient` instance to be used for auth requests.
- May be overridden by subclasses to use an http client other than
+ May be overridden by subclasses to use an HTTP client other than
the default.
"""
return httpclient.AsyncHTTPClient()
post_args=None, **args):
"""Fetches the given relative API path, e.g., "/btaylor/picture"
- If the request is a POST, post_args should be provided. Query
+ If the request is a POST, ``post_args`` should be provided. Query
string arguments should be given as keyword arguments.
An introduction to the Facebook Graph API can be found at
http://developers.facebook.com/docs/api
- Many methods require an OAuth access token which you can obtain
- through authorize_redirect() and get_authenticated_user(). The
- user returned through that process includes an 'access_token'
- attribute that can be used to make authenticated requests via
- this method. Example usage::
+ Many methods require an OAuth access token which you can
+ obtain through `~OAuth2Mixin.authorize_redirect` and
+ `get_authenticated_user`. The user returned through that
+ process includes an ``access_token`` attribute that can be
+ used to make authenticated requests via this method.
+
+ Example usage::
class MainHandler(tornado.web.RequestHandler,
tornado.auth.FacebookGraphMixin):
callback(escape.json_decode(response.body))
def get_auth_http_client(self):
- """Returns the AsyncHTTPClient instance to be used for auth requests.
+ """Returns the `AsyncHTTPClient` instance to be used for auth requests.
- May be overridden by subclasses to use an http client other than
+ May be overridden by subclasses to use an HTTP client other than
the default.
"""
return httpclient.AsyncHTTPClient()