Changes between Initial Version and Version 1 of MultipleAuthBackends


Ignore:
Timestamp:
Feb 26, 2006, 11:37:48 PM (18 years ago)
Author:
jkocherhans
Comment:

initial and very rough revision of multiple auth backends

Legend:

Unmodified
Added
Removed
Modified
  • MultipleAuthBackends

    v1 v1  
     1= Multiple Authentication Backends =
     2
     3Currently, Django's authentication system assumes that all your users live in the db as Django models and you must use a username and password to login. This works ok until you realize that you need to support 200 users who already have accounts Active Directory (or insert your favorite windows-free LDAP server here.) In short, Django shouldn't require you to use Django models for users, and it shouldn't require you to use a username and password. What if I want to use an email address, or use some other unique key for my boss REST interface?
     4
     5Zope has a pretty simple/cool solution for this. It's not really standalone, but it's not that hard to port either. Regardless of what you think of Zope, they've been doing this longer than most of us, and we could probably learn a thing or two from them. Most (meaning nearly all) of these ideas are modeled on zope.app.authentication
     6
     7The details are a little flaky, and the naming kinda sucks, please make suggestions. Basically, here's how it goes:
     8
     9== Credentials ==
     10
     11Instead of hard-coding checks for a cookie, or a username/password etc. etc. let's let different callables just grab the credentials from the request. In fact, let's put a bunch of these plugins in a list, so if the first one fails, we can try another.
     12
     13{{{
     14#!python
     15
     16class UsernamePasswordPlugin:
     17    def extract_credentials(self, request):
     18        return {'username': request.POST['username'], 'password': request.POST['password']}
     19
     20}}}
     21
     22We can add plugins for extracting credentials from a sessions, getting an api key, whatever, and configure them like so:
     23
     24Add this to settings.py:
     25{{{
     26#!python
     27
     28CREDENTIALS_PLUGINS = (
     29    'django.contrib.auth.SessionCredentials',
     30    'django.contrib.auth.UsernamePasswordCredentials',
     31)
     32}}}
     33
     34== Authentication ==
     35
     36Credendials only get us half-way there. We'd better check them against something. How about a list of authentication backends:
     37
     38Add this to settings.py:
     39{{{
     40#!python
     41
     42AUTHENTICATION_PLUGINS = (
     43    'django.contrib.auth.ModelAuthenticator',
     44    'django.contrib.auth.LDAPAuthenticator',
     45)
     46}}}
     47
     48And now for the plugin:
     49{{{
     50#!python
     51
     52from django.contrib.auth.models import User
     53
     54class ModelAuthenticator
     55    def authenticate_credentials(self, credentials):
     56        #we need a username and password in the credentials, if they're not there, return None
     57        #keep in mind that other authenticators can look for different types of credentials
     58        #if the username or password or bad, return None
     59        #if everything is kosher, return the USER
     60}}}
     61
     62So Zope uses a factory to create a user. Your auth plugin can do whatever you feel like as long as it returns a user object. (We need to come up with a minimum informal interface for a user object. Use duck typing, not formal interfaces. The user does not ''need'' to be a django model object, but it ''can'' be. Read the last sentence again.) So if we're writing an LDAP plugin, we could make one that actually creates a Django model the first time an LDAP user successfully logs in. We could use events/factories to make this even more abstract and flexible. Let's lay the groundwork first though.
     63
     64
     65== Multiple Backends ==
     66
     67Um, so what if I want to use LDAP '''and''' Django models? Funny you should ask.
     68
     69{{{
     70#!python
     71
     72class AuthenticationUtility:
     73    def authenticate(self, request):
     74        #psuedocode ahead!
     75        for cred_plugin in credentials_plugins:
     76             credentials = cred_plugin.extract_credentials(request)
     77             if credentials is not None:
     78                 for auth_plugin in auth plugins:
     79                     user = auth_plugin.authenticate_credentials(credentials)
     80                     if user is not None:
     81                         return user
     82}}}
     83
     84This is the main interface to the auth system. In english, check for credentials, if we find them, try to authenticate them against every auth backend in settings.py, if none of them authenticate, do it all again with the next type of credentials.
     85
     86Credentials plugins should probably have a logout method. I didn't give any examples, but it shouldn't be hard.
     87
     88That's most of it. Think you have a use case this won't work for? Please tell django-developers.
Back to Top