Twisted Documentation: HTTP Authentication with Twisted.Web2

# HTTP Authentication with Twisted.Web2

## 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

    class IHTTPUser(Interface):
        pass

    class HTTPUser(object):
        implements(IHTTPUser)

    class HTTPAuthRealm(object):
        implements(portal.IRealm)

        def requestAvatar(self, avatarId, mind, *interfaces):
            if IHTTPUser in interfaces:
                return IHTTPUser, HTTPUser()

            raise NotImplementedError("Only IHTTPUser interface is supported")

Listing 1: Cred Setup - ../examples/auth/credsetup.py

## Credential Factories

Credential Factories as defined by `ICredentialFactory` are the heart of HTTP Authentication. Their functions are two-fold:

1. They provide the challenges and decode the responses from the client, while maintaining state for stateful authentication schemes such as Digest.
2. 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 a `LoginFailed` 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 the `ICredentials` returned by the specified `ICredentialFactory` failed.

### Usage By Example

    from twisted.web2 import channel, resource, http, responsecode, server

    class ProtectedResource(resource.Resource):
        def render(self, req):
            return http.Response(responsecode.OK,
                                 stream=("Hello, you've successfully accessed "
                                         "a protected resource."))

    from twisted.web2.auth import digest, basic, wrapper

    from twisted.cred.portal import Portal
    from twisted.cred import checkers

    import credsetup

    portal = Portal(credsetup.HTTPAuthRealm())

    checker = checkers.InMemoryUsernamePasswordDatabaseDontUse(guest='guest123')

    portal.registerChecker(checker)

    root = wrapper.HTTPAuthResource(ProtectedResource(),
                                    (basic.BasicCredentialFactory('My Realm'),
                                     digest.DigestCredentialFactory('md5',
                                                                   'My Realm')),
                                    portal, (credsetup.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)

Listing 2: Working HTTPAuthResource Example - ../examples/auth/httpauth.tac

This simple example consists of the following application specific components.

1. A Resource we wish to protect from unauthorized access, in this case it is our ProtectedResource
2. 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.
3. A single `ICredentialFactory`, in this case a `DigestCredentialFactory` using the md5 algorithm and with a realm of `"My Realm"`
4. 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.

Index

Version: 0.2.0