So, here's a cunning plan. There's already a Man-In-The-Middle (the proxy). This is already slightly active at moments during the session. My plan is to make this proxy a little more active when connection establishment is being performed. In particular ...
*) Use the EXTERNAL SASL mechanism to indicate that the authentication is happening over an external channel.
*) The proxy runs at a URL which is optionally protected by some form of proxiable authentication (the one I'm interested in here is Kerberos, but other options are possible).
*) If the user has successfully authenticated to the proxy, then it looks out for the stream:features packet at the start of the XMPP handshake. It intercepts this packet, and adds EXTERNAL to the _start_ of the list of supported SASL mechanisms.
*) The client then picks which authentication mechanism to use. If it is a hacked version of JWChat, it will try EXTERNAL
*) The proxy looks out for an attempt at doing EXTERNAL. If there is one, it doesn't forward this packet to the server. Instead, it starts its own GSSAPI based authentication, using the current user's credentials. It talks directly to the XMPP server (without returning any packets to the client) until the GSSAPI handshake is complete. It then fakes success or failure to the client as coming from the EXTERNAL mechanism.
This should all work, with a few caveats. We can't establish security layers (unless the proxy becomes really clever). The proxy needs to know about a Kerberised authentication mechanism (in our case, probably NegotiateAuth), and about how to do the SASL GSSAPI mechanism. The proxy needs to decode, and encode, XMPP packets by itself.
I'm going to have a go at implementing this using Punjab as the proxy (as Python is slightly less unfamiliar than Java at the moment, and there is at least a free SPNEGO / NegotiateAuth plugin for Twisted available in the Apple CalDav source)