Overview
twisted.web2.auth
implements Digest
and Basic HTTP Authentication as specified
by RFC 2617.
This document attempts to describe:
- How
twisted.cred
is used to support pluggable authentication for HTTP. - How the
ICredentialFactory
Interface is used to define authentication schemes. - How to
use
HTTPAuthResource
to protect a portion or all of your resource tree.
Cred
twisted.cred
is a pluggable
authentication framework which allows application/protocol
developers to easily support multiple authentication types
regardless of the backend used. This document assumes some
familiarity with cred and suggests you
read Cred:
Pluggable Authentication
and the twisted.cred
API Reference for
further information. However several of the application specific
implementations of objects required by cred
are listed below.
from zope.interface import Interface, implements from twisted.cred import portal from twisted.web2.auth.interfaces import IHTTPUser class HTTPUser(object): """ A user that authenticated over HTTP Auth. """ implements(IHTTPUser) username = None def __init__(self, username): """ @param username: The str username sent as part of the HTTP auth response. """ self.username = username class HTTPAuthRealm(object): implements(portal.IRealm) def requestAvatar(self, avatarId, mind, *interfaces): if IHTTPUser in interfaces: return IHTTPUser, HTTPUser(avatarId) raise NotImplementedError("Only IHTTPUser interface is supported")
Credential Factories
Credential Factories as defined
by ICredentialFactory
are
the heart of HTTP Authentication. Their functions are two-fold:
- They provide the challenges and decode the responses from the client, while maintaining state for stateful authentication schemes such as Digest.
- They are used to define and determine which authentication schemes should be used during authentication
The ICredentialFactory
interface defines the following:
scheme
an attribute containing the textual representation of the scheme name, for HTTP this will be used in the www-authenticate and authorization headers.
getChallenge
(peer)called whenever a new challenge is to be issued, the argument peer may be used to calculate a unique challenge based on the
IAddress
of the client.decode
(response, method=None)called with a string response (such as the body of an Authorization header) and (for HTTP based protocols) the HTTP method by which the initial response was sent, such as GET, POST, REGISTER, etc. This will either return an
ICredential
or raise aLoginFailed
Exception if it is unable to decode the response.
The HTTPAuthResource
The purpose of HTTPAuthResource
is to
trap both locateChild
and
renderHTTP
and require
authentication before allowing requests to pass on to it's
wrappedResource. It does this by returning an
UnauthorizedResource
if
the following conditions are not met:
- No Authorization header was given
- The Authorization header contained an unsupported scheme.
- The Authorization header response was not able to be decoded
by the
ICredentialFactory
for the specified scheme - The
checkPassword
for theICredentials
returned by the specifiedICredentialFactory
failed.
Usage By Example
from twisted.web2 import channel, resource, http, responsecode, server from twisted.web2.auth.interfaces import IAuthenticatedRequest, IHTTPUser class ProtectedResource(resource.Resource): """ A resource that is protected by HTTP Auth """ addSlash = True def render(self, req): """ I adapt C{req} to an L{IAuthenticatedRequest} before using the avatar to return a personalized message. """ avatar = IAuthenticatedRequest(req).avatar return http.Response( responsecode.OK, stream=("Hello %s, you've successfully accessed " "a protected resource." % (avatar.username,))) from twisted.web2.auth import digest, basic, wrapper from twisted.cred.portal import Portal from twisted.cred import checkers import credsetup # # Create the portal with our realm that knows about the kind of avatar # we want. # portal = Portal(credsetup.HTTPAuthRealm()) # # Create a checker that knows about the type of backend we want to use # and that knows about the ICredentials we get back from our # ICredentialFactories. And tell our portal to use it. # checker = checkers.InMemoryUsernamePasswordDatabaseDontUse(guest='guest123') portal.registerChecker(checker) # # Set up our HTTPAuthResource, we have to tell it the root of the resource # heirarchy we want to protect, as well as the credential factories we want # to support, the portal we want to use for logging in, and the interfaces # that IAuthenticatedRequest.avatar to may implement. # root = wrapper.HTTPAuthResource(ProtectedResource(), (basic.BasicCredentialFactory('My Realm'), digest.DigestCredentialFactory('md5', 'My Realm')), portal, (IHTTPUser,)) site = server.Site(root) # Start up the server from twisted.application import service, strports application = service.Application("HTTP Auth Demo") s = strports.service('tcp:8080', channel.HTTPFactory(site)) s.setServiceParent(application)
This simple example consists of the following application specific components.
- A Resource we wish to protect from unauthorized access, in this case it is our ProtectedResource
- A portal using our realm from Listing 1, and having a
single
ICredentialCheckers
. In this case a simple checker that stores usernames and passwords in memory and should not be used for anything other than as an example. - A single
ICredentialFactory
, in this case aDigestCredentialFactory
using the md5 algorithm and with a realm of"My Realm"
- A sequence of avatar interfaces consisting of our IHTTPUser as defined in Listing 1
Things HTTPAuthResource doesn't do
HTTPAuthResource
is provided
largely as a lowest common denominator authentication solution. As a
result, it has a few limitations:
- Never gives up on the client
There's no limit to the number of authentication attempts that a client can make.
HTTPAuthResource
will always respond to a failed authentication with a 401 UNAUTHORIZED message, and the client will always be issued a new challenge to which it is welcome to respond again and again. A better solution, not yet implemented, would be to issue a 403 FORBIDDEN response code for a particular client at some point. - Doesn't propagate the Avatar
Currently nothing is done with the avatar returned by a successful login. In a future version, this avatar may be attached to the request so that resources below it benefit from the information.
- Method limiting
Currently there is no provided means of limiting only a subset of possible HTTP methods to require HTTP Authentication. The HTTP mmethods GET, PUT, POST are all treated exactly the same, which permits only the choices of a total lockdown of the resource or leaving it wide open.
As a result of these limitations HTTPAuthResource
is provided more
as an example of how you can work with twisted.web2.auth
rather than as a definitive
solution.