Hello Quan Here are my thoughts.
I think AuthHook encompasses two things: - A bad implementation of SASL (no advertized capability) which is Ok dropping. We could potentially even propose a SaslAuthHookAdapter that plugs in the SASL flow and delegates to the given AuthHook thus easing migration? It would then be mostly conf to adapt ones axtension... - Decorate login eg to push a notif somewhere. Maybe we culd have a new hook (SaslAuthResultHook ?) for being able to decorate this and allow adding side effect effectively? So I'd go with: - Deprecate AuthHook - Introduce a SaslAuthHookAdapter that one can extend to record existing AuthHook. The SaslAuthHookAdapter should be flexible enough to allow override the PLAIN mechanism. - Have a new hook: SaslAuthResultHook - Write an upgrade-instructions regarding this. Would this make sense ?-- Best regards, Benoit TELLIER General manager of Linagora VIETNAM. Product owner for Twake-Mail product. Chairman of the Apache James project. Mail: [email protected] Tel: (0033) 6 77 26 04 58 (WhatsApp, Signal) On Jun 22, 2026 12:08 PM, from Quan Tran Hong <[email protected]>Hello folks, Following the Generic SASL modularization and adoption for IMAP at github.com/apache/james-project/pull/3059, I am working further on the adoption for SMTP (based on the IMAP PR) to see how it goes, before we can decide if it is good to merge the IMAP PR and go forward with this SASL refactoring. Regarding SMTP adoption, I am not sure if we should drop the `AuthHook` interface (and its extension capability). *If we drop `AuthHook` for SMTP*, then we would rely purely on the SASL layer for the authentication logic, which should reduce the code complexity. However, community `AuthHook` extension implementors would then need to be rewritten under a custom `SaslMechanism` implementation instead. Example of what we proposed for IMAP custom SASL: github.com/apache/james-project/pull/3059/changes/3f0a554e4c3b3914daecbd41b75a6aa21ae9c8cf . Also, please note that it may not be straightforward to just replace `AuthHook` with the `SaslMechanism` extension: AuthHook can extend the *PLAIN* auth capability, while SaslMechanism would likely be distinct by the mechanism name (PLAIN) and only override is possible. *If we keep `AuthHook` for SMTP,* we would mix the legacy `AuthHook` code and the modular SASL layer for SMTP authentication, which may introduce extra complexity for SMTP authentication. Also, having 2 mechanisms to configure authentication for SMTP (`AuthHook` and `SaslMechanism`) feels odd. However, keeping the `AuthHook` would be less invasive for community extension usage, if any. I am not sure about the `AuthHook` extension usage in our community, and the best way to go for SMTP adoption here. Maybe I would go the defensive way to try to keep the `AuthHook` mechanism, to avoid invasion. Or do you think the code complexity and the limited adoption of `AuthHook` are not worth keeping? Feedback and opinion are very welcome! Quan On Wed, Jun 3, 2026 at 12:30 PM Benoit TELLIER <[email protected]> wrote: > This looks really reasonable to me. > Other mail servers achieve sasl code mutualisation and expose it as > extension. > I'd have naturaly bundled the side effects behind the sasl API to mimic > current authentication code but I am actually curious to see where the more > descriptive approach leads us to! > -- > > > Best regards, > > Benoit TELLIER > > General manager of Linagora VIETNAM. > Product owner for Twake-Mail product. > Chairman of the Apache James project. > > Mail: [email protected] > Tel: (0033) 6 77 26 04 58 (WhatsApp, Signal) > > > > On Jun 3, 2026 5:22 AM, from Quan Tran Hong <[email protected]>Hi > all, > > I would like to propose a generic SASL mechanism extension model for James > protocols. > > Today, when adding a new authentication mechanism for IMAP or SMTP, we need > to modify core protocol code, such as IMAP *AuthenticateProcessor* or SMTP > *AuthCmdHandler*. This makes the protocol handlers accumulate > mechanism-specific branches, and would make future mechanisms such as > GSSAPI / Kerberos harder to add cleanly. > > The goal is: > > - adding a new SASL mechanism should not require modifying core IMAP/SMTP > command handlers; > - protocol code should keep protocol framing and side effects; > - SASL mechanism code should own mechanism semantics and exchange state; > - existing IMAP/SMTP behavior should remain unchanged when no new > configuration is provided. > > This takes inspiration from JMAP authentication strategies configuration > and Guice loading. > > > *Configuration shape* > ------------------- > > Each protocol would keep an operator-visible *auth.saslMechanisms* list. > > Example for IMAP: > > > > * <auth> > > <saslMechanisms>PlainSaslMechanism,OauthBearerSaslMechanism,XOauth2SaslMechanism,com.example.james.kerberos.GssapiSaslMechanism</saslMechanisms> > </auth>* > > Example for SMTP: > > > > * <auth> > > <saslMechanisms>LoginSaslMechanism,PlainSaslMechanism,OauthBearerSaslMechanism,XOauth2SaslMechanism,com.example.james.kerberos.GssapiSaslMechanism</saslMechanisms> > </auth>* > > Expected semantics: > > - if *auth.saslMechanisms* is absent, load current protocol defaults; > - IMAP defaults remain PLAIN, OAUTHBEARER, XOAUTH2; > - SMTP defaults remain LOGIN, PLAIN, OAUTHBEARER, XOAUTH2; > - if configured, load the configured list in the configured order; > - simple class names resolve against James default SASL packages; > - fully qualified class names allow external/community extensions; > > Existing protocol settings still decide availability. For example, plain > auth restrictions still decide whether PLAIN is advertised, OIDC > configuration still decides whether OAuth mechanisms are advertised, and > SMTP *auth.announce* still controls whether SMTP advertises AUTH. > > > *Proposed SPI shape* > ------------------ > > I propose introducing protocol-neutral SASL types in *protocols/api*, for > example under *org.apache.james.protocols.api.sasl*. > > The core idea is a stateful SaslExchange: each SASL mechanism creates one > exchange per authentication attempt, and that exchange owns mechanism state > across the initial request and continuation responses. > > Core structure: > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > * interface SaslMechanism { String name(); boolean > supports(SaslProtocol protocol); boolean > isAvailable(SaslSessionContext context); SaslExchange > start(SaslInitialRequest request, SaslSessionContext context); } enum > SaslProtocol { IMAP, SMTP, MANAGESIEVE, POP3 } record > SaslInitialRequest(SaslProtocol protocol, String mechanismName, > Optional<byte[]> initialResponse) { } record SaslIdentity(Username > authenticationId, Username authorizationId) { } interface > SaslSessionContext { SaslProtocol protocol(); boolean > isTlsStarted(); <T> Optional<T> configuration(Class<T> > configurationType); } interface SaslExchange extends AutoCloseable { > SaslStep firstStep(); SaslStep onResponse(byte[] > clientResponse); void abort(); @Override void > close(); } interface SaslStep { record > Challenge(Optional<byte[]> payload) implements SaslStep { } > record Success(SaslIdentity identity, Optional<byte[]> serverData, String > log) implements SaslStep { } record Failure(String log) > implements SaslStep { } }* > > The intended split is: > > - SASL mechanisms own payload parsing, challenge generation, response > validation, final SASL identity extraction, and exchange state. > - IMAP/SMTP bridges own base64/wire framing, continuation responses, > success/failure session side effects, audit logs, hooks, and > protocol-specific error responses. > > For example, PLAIN and OAUTHBEARER can complete from *firstStep()*, while > GSSAPI can return Challenge first and complete later from > *onResponse(...)*. > > SaslIdentity carries both the authentication identity and the authorization > identity. This keeps delegation expressible by the SPI without granting it > automatically. IMAP/SMTP bridges still decide whether delegation is allowed > and how to build their protocol session state. > > > *Loading and registry* > -------------------- > > Mechanism loading would follow the same spirit as JMAP authentication > strategies: > > > > > > > > > > > > > > * interface SaslMechanismLoader { ImmutableList<SaslMechanism> > load(Collection<String> classNames); } class SaslMechanismRegistry { > Optional<SaslMechanism> find(String mechanismName, SaslProtocol > protocol) { // match by mechanism name and supports(protocol) > } Stream<SaslMechanism> availableFor(SaslProtocol protocol, > SaslSessionContext context) { // filter supports(protocol) and > isAvailable(context) } }* > > Then implement a GuiceSaslMechanismLoader to actually load the SASL > mechanisms. > > > *Incremental implementation plan* > ------------------------------- > > I suggest doing this step by step: > > - *Step 1*: Start with a POC introducing the shared SaslMechanism SPI and > adapting IMAP. > This must not introduce breaking changes to existing authentication > configs. The SASL SPI can be adopted gradually, protocol by protocol, > without changing behavior for protocols that have not adopted it yet. > - *Step 2*: Adapt SASL modularization for SMTP. > - *Step 3*: Leverage the SASL modularization to implement GSSAPI / Kerberos > mechanism support. > > ManageSieve and POP3 are not part of the immediate scope, but the SPI > should make later adoption possible. > > *Testing expectations* > -------------------- > > At minimum, I think we should prove: > > - no breaking change: absent auth.saslMechanisms keeps existing IMAP/SMTP > SASL methods; > - configured custom SASL mechanism loads and authenticates through real or > near-real protocol wiring; > - authentication / authorization identity handling preserves existing > delegation behavior (we should already have tests for these); > - fake multi-step mechanism works for continuation; > - exchange cleanup is covered. > > *Feedback requested* > ------------------ > > Does this refactoring direction look reasonable to you? Feedback and > discussion are welcome! > Meanwhile, I would start POC work on my side to see how this design goes... > > Regards, > Quan > >
