Index: docs/howto/apache-auth.txt
===================================================================
--- docs/howto/apache-auth.txt	(revision 17371)
+++ docs/howto/apache-auth.txt	(working copy)
@@ -2,13 +2,6 @@
 Authenticating against Django's user database from Apache
 =========================================================
 
-.. warning::
-
-    Support for mod_python has been deprecated within Django. At that
-    time, this method of authentication will no longer be provided by
-    Django. The community is welcome to offer its own alternate
-    solutions using WSGI middleware or other approaches.
-
 Since keeping multiple authentication databases in sync is a common problem when
 dealing with Apache, you can configuring Apache to authenticate against Django's
 :doc:`authentication system </topics/auth>` directly. For example, you
@@ -24,11 +17,120 @@
 .. _Subversion: http://subversion.tigris.org/
 .. _mod_dav: http://httpd.apache.org/docs/2.0/mod/mod_dav.html
 
-Configuring Apache
-==================
+Authentication with mod_wsgi
+============================
 
-To check against Django's authorization database from a Apache configuration
-file, you'll need to use mod_python's ``PythonAuthenHandler`` directive along
+Make sure that mod_wsgi is installed and activated and that you have
+followed the steps to setup
+:doc:`Apache with mod_wsgi <deployment/wsgi/modwsgi>`
+
+Next, edit your Apache configuration to add a path that you want
+only authenticated users to be able to view: 
+
+.. code-block:: apache
+
+    WSGIScriptAlias / /path/to/mysite/config/mysite.wsgi
+    
+    WSGIProcessGroup %{GLOBAL}
+    WSGIApplicationGroup django
+    
+    <Location "/secret">
+        AuthType Basic
+        AuthName "Top Secret"
+        Require valid-user
+        AuthBasicProvider wsgi
+        WSGIAuthUserScript /path/to/mysite/config/mysite.wsgi
+    </Location>
+
+The ``WSGIAuthUserScript`` directive tells mod_wsgi to execute the 
+``check_password`` function in that script passing the user name and
+password that it receives from the prompt. In this example,
+the ``WSGIAuthUserScript`` is the same as the ``WSGIScriptAlias`` that
+defines your application. 
+
+.. admonition:: Using Apache 2.2 with authentication
+
+    Make sure that ``mod_auth_basic`` and ``mod_authz_user`` are loaded.
+
+    These might be compiled statically into Apache, or you might need to use 
+    LoadModule to load them dynamically in your ``httpd.conf``:
+
+    .. code-block:: apache
+        
+        LoadModule auth_basic_module modules/mod_auth_basic.so
+        LoadModule authz_user_module modules/mod_authz_user.so
+
+Finally, edit your WSGI auth script ``mysite.wsgi`` to tie Apache's  
+authentication to yoursite's users:
+
+.. code-block:: python
+    
+    import os
+    import sys
+    
+    os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings'
+
+    from django.contrib.auth.handlers.modwsgi import check_user
+    
+    from django.core.handlers.wsgi import WSGIHandler
+    application = WSGIHandler()
+
+
+Requests beginning with ``/secret/`` will now require a user to authenticate.
+
+The mod_wsgi `access control mechanisms documentation`_ provides additional
+details and information about alternative methods of authentication.
+
+.. _access control mechanisms documentation: http://code.google.com/p/modwsgi/wiki/AccessControlMechanisms
+
+Authorization with mod_wsgi and Django groups
+---------------------------------------------
+
+In addition, mod_wsgi also provides functionality to restrict a particular 
+location to members of a group.
+
+In this case, the Apache configuration should look like this:
+
+.. code-block:: apache
+
+    WSGIScriptAlias / /path/to/mysite/config/mysite.wsgi
+    
+    WSGIProcessGroup %{GLOBAL}
+    WSGIApplicationGroup django
+    
+    <Location "/secret">
+        AuthType Basic
+        AuthName "Top Secret"
+        AuthBasicProvider wsgi
+        WSGIAuthUserScript /path/to/mysite/config/mysite.wsgi
+        WSGIAuthGroupScript /path/to/mysite/config/mysite.wsgi
+        Require group secret-agents
+        Require valid-user
+    </Location>
+    
+Because of the ``WSGIAuthGroupScript`` directive, the same WSGI auth script 
+``mysite.wsgi`` must also import the method ``groups_for_user`` which
+returns a list of the user's groups.
+
+.. code-block:: python
+    
+    from django.contrib.auth.handlers.modwsgi import check_user, groups_for_user
+    
+Requests for ``/secret/`` will now also require a user to a member of the
+"secret-agents" group.
+
+Authentication with mod_python
+==============================
+
+.. warning::
+
+    Support for mod_python has been deprecated within Django. At that
+    time, this method of authentication will no longer be provided by
+    Django. The community is welcome to offer its own alternate
+    solutions using WSGI middleware or other approaches.
+
+To check against Django's authorization database from mod_python, 
+you'll need to use mod_python's ``PythonAuthenHandler`` directive along
 with the standard ``Auth*`` and ``Require`` directives:
 
 .. code-block:: apache
@@ -89,8 +191,8 @@
             PythonAuthenHandler django.contrib.auth.handlers.modpython
         </Location>
 
-By default, the authentication handler will limit access to the ``/example/``
-location to users marked as staff members.  You can use a set of
+By default, the mod_python authentication handler will limit access to the 
+``/example/`` location to users marked as staff members.  You can use a set of
 ``PythonOption`` directives to modify this behavior:
 
 ================================  =========================================
Index: django/contrib/auth/handlers/modwsgi.py
===================================================================
--- django/contrib/auth/handlers/modwsgi.py	(revision 0)
+++ django/contrib/auth/handlers/modwsgi.py	(revision 0)
@@ -0,0 +1,41 @@
+from django.contrib.auth.models import User
+from django import db
+
+def check_password(environ, username, password):
+    """
+    Authenticates against Django's auth database
+    """
+
+    db.reset_queries() 
+
+    try: 
+        # verify the user exists
+        try: 
+            user = User.objects.get(username=username, is_active=True) 
+        except User.DoesNotExist: 
+            return None
+
+        # verify the password for the given user
+        if user.check_password(password): 
+            return True
+        else: 
+            return False
+    finally: 
+        db.close_connection()
+
+def groups_for_user(environ, username): 
+    """
+    Authorizes a user based on groups
+    """
+
+    db.reset_queries() 
+
+    try:
+        try:  
+            user = User.objects.get(username=username, is_active=True)
+        except User.DoesNotExist:  
+            return []
+
+        return [group.name.encode('utf-8') for group in user.groups.all()]
+    finally:
+        db.close_connection()
Index: django/contrib/auth/tests/__init__.py
===================================================================
--- django/contrib/auth/tests/__init__.py	(revision 17371)
+++ django/contrib/auth/tests/__init__.py	(working copy)
@@ -17,5 +17,6 @@
 from django.contrib.auth.tests.views import (AuthViewNamedURLTests, 
     PasswordResetTest, ChangePasswordTest, LoginTest, LogoutTest, 
     LoginURLSettings)
+from django.contrib.auth.tests.handlers import ModWsgiHandlerTestCase
 
 # The password for the fixture data users is 'password'
Index: django/contrib/auth/tests/handlers.py
===================================================================
--- django/contrib/auth/tests/handlers.py	(revision 0)
+++ django/contrib/auth/tests/handlers.py	(revision 0)
@@ -0,0 +1,43 @@
+from django.contrib.auth.handlers.modwsgi import check_password, groups_for_user
+from django.contrib.auth.models import User, Group
+from django.test import TestCase
+
+class ModWsgiHandlerTestCase(TestCase):
+    """
+    Tests for the mod_wsgi authentication handler
+    """
+
+    def setUp(self):
+        user1 = User.objects.create_user('test', 'test@example.com', 'test')
+        user2 = User.objects.create_user('test1', 'test1@example.com', 'test1')
+
+        group = Group.objects.create(name='test_group')
+        user1.groups.add(group)
+        
+
+    def testCheckPassword(self):
+        """
+        Verify that check_password returns the correct values as per
+        http://code.google.com/p/modwsgi/wiki/AccessControlMechanisms#Apache_Authentication_Provider
+        """
+
+        # User not in database
+        self.assertTrue(check_password({}, 'unknown', '') is None)
+
+        # Valid user with correct password
+        self.assertTrue(check_password({}, 'test', 'test'))
+
+        # Valid user with incorrect password
+        self.assertFalse(check_password({}, 'test', 'incorrect'))
+
+    def testGroupsForUser(self):
+        """
+        Check that groups_for_user returns correct values as per
+        http://code.google.com/p/modwsgi/wiki/AccessControlMechanisms#Apache_Group_Authorisation
+        """
+
+        # User not in database
+        self.assertEqual(groups_for_user({}, 'unknown'), [])
+
+        self.assertEqual(groups_for_user({}, 'test'), ['test_group'])
+        self.assertEqual(groups_for_user({}, 'test2'), [])
