David Reid wrote:
But in Basic and Digest auth you don't have the username until you get the response to your challenge. So this is where IAuthorizer comes in it handles all the steps prior to having something that you can use to build a credentials.
I'm no cred expert, and I dislike it conceptually, but as far as I can tell it's got all the facilities it needs. HTTP is slightly more complex so I'll start with a SASL-ised imap-like example, to see if I've got the right idea: class simpleproto(basic.LineReceiver): def lineReceived(self, line): if self.creds: # forward to CR module that's in progress self.creds.lineReceived(line) else: cmd, rest = line.split(' ', 1) fn = getattr(self, 'do_'+cmd, None) if not fn: self.transport.write('NO unknown command %s\n' % (cmd,)) self.transport.loseConnection() def do_AUTHENTICATE(self, mechanism): if mechanism=='GSSAPI': self.creds = ChallengeResponse(self) return self.portal.login(self.creds).addCallbacks(self.authok, self.authfail) def authok(self, avatar): self.avatar = avatar self.lineReceived = avatar.lineReceived def authfail(self, why): self.transport.write(why.getErrorMessage()+'\n') self.transport.loseConnection() class ChallengeResponse: def __init__(self, conn): self.conn = conn self.started = False self.context = hypotheticalGssapiModule.init() def lineReceived(self, line): done, response = self.context.input(line.decode('base64')) self.conn.transport.write(response.encode('base64')+'\n') if done: if self.context.success(): self.finish.callback(self.context.getPrincipal()) else: self.finish.errback(self.context.errMsg()) class GssapiChecker: implements(checkers.ICredentialsChecker) def requestAvatarId(self, creds): creds.finish = defer.Deferred() return creds.finish The neat thing about cred is that the code above (if it worked at all, which it would with only slight fiddling) doesn't require the portal or realm to change. Similarly, only a bit of fiddling permits multi-method auth e.g. for IMAP brokenness: def do_LOGIN(self, data): user, pass = data.split(' ', 1) creds = credentials.UsernamePassword(user, pass) return self.portal.login(creds).addCallbacks(...) ...and add an appropriate checker to the portal. HTTP is a bit of a pain because of the "connectionless" basis. The RFCs for the hacky mechanisms like "Negotiate" (the MS-ism for kerberos over HTTP) and such show that. Digest would want some kind of stateless version - you're effectively authenticating requests as opposed to the connection, but the basic principle is the same. I'm sure you know all this. So am I missing something? It looks like cred needs no extending?