JAMES-4210 Generic SASL mechanism extension#3059
Conversation
Introduce a protocol-neutral SASL SPI in `protocols/api`. The new API models SASL as a stateful exchange with protocol-neutral initial requests, continuation steps, success/failure results, and authentication identities. It also exposes password and bearer-token authentication service contracts through `SaslSessionContext` so future mechanism implementations do not depend directly on IMAP or SMTP classes. Add contract tests covering one-step mechanisms, multi-step mechanisms, password-like authentication through the context service contract, delegated identities, defensive byte-array copying, and exchange cleanup.
Add a minimal IMAP bridge for the shared SASL SPI. The bridge keeps IMAP-specific wire handling outside the generic SPI: it converts IMAP AUTHENTICATE input to SaslInitialRequest, handles the SASL-IR "=" empty initial response marker, base64-encodes challenge continuations, decodes client continuation lines, and wires abort/close lifecycle handling around SaslExchange. Add unit tests for initial response decoding, continuation formatting, client response decoding, and exchange cleanup.
Add a minimal SMTP bridge for the shared SASL SPI. The bridge keeps SMTP-specific AUTH framing outside the generic SPI: it converts SMTP AUTH initial responses to SaslInitialRequest, handles the "=" empty initial response marker, maps SASL challenges to SMTP 334 responses, decodes client continuation lines, and wires abort/close lifecycle handling around SaslExchange. Add unit tests for initial response decoding, SMTP challenge formatting, client response decoding, and exchange cleanup.
Introduce reusable PLAIN, OAUTHBEARER and XOAUTH2 SASL mechanisms in protocols-api. Add the service-factory SPI so mechanisms can declare protocol-provided services and the registry can initialize them per session.
Refactor IMAP AuthenticateProcessor to use SaslMechanismRegistry while preserving direct non-Guice defaults. Move IMAP password and bearer-token authentication into protocol service factories and keep mailbox session and failure details in the IMAP SASL session context.
Wire SASL mechanism loading into the Guice IMAP server module. Add a default mechanism class-name provider and an extension service-factory provider so custom SASL extensions can load their own auth configuration from imapserver.xml.
Allow SmtpSaslBridge to be reused with a configured SASL protocol and add LMTP to SaslProtocol for future LMTP AUTH support.
Load extra IMAP SASL authentication service factory providers from auth.saslAuthenticationServiceFactoryProviderExtensions. Providers are Guice-instantiated, merged with built-in providers, and can parse their own IMAP auth configuration.
Add an EXAMPLE-TOKEN SASL mechanism to examples/custom-imap to demonstrate a custom mechanism, provider extension, and auth.exampleToken configuration. Add dedicated tests for custom SASL advertisement, successful custom auth, invalid-token rejection, and preserving built-in PLAIN authentication.
14ba04f to
cb204cf
Compare
|
Hello, I did try a lot to make the POC ready so far, for IMAP. I did rebase on the latest master to have the You can just jump to the last commit JAMES-4210 Add custom IMAP SASL extension example to see how we could implement an SASL mechanism extension for IMAP. Note that I did rush to make this POC alive for IMAP, so of course, review + polish and double-check would be needed later :) |
Remove the staged SMTP bridge experiment from this IMAP-focused simplification series. SMTP adoption remains a later step.
Update the custom IMAP extension example to contribute a SASL mechanism factory through Guice, keep auth.saslMechanisms configuration, and cover continuation plus final server-data behavior.
|
Hello, I did simplify the design a lot. I think it should be more or less OK now. Happy to receive any further remarks. |
chibenwa
left a comment
There was a problem hiding this comment.
Sorry to be picky, but this work shall be simplified further.
Let's invest more time on it.
Replace the credential-returning SASL SPI with exchange-driven authentication results. Add protocol-neutral success/failure/authenticator contracts and make abort close exchanges by default.
Add protocols/sasl for reusable James SASL implementations, factories, transport policy, and James auth/authz integration. Move PLAIN and OIDC mechanisms out of protocols/api.
Resolve configured SASL mechanism factories through Guice, support built-in simple names, preserve configured order, and de-duplicate mechanism names case-insensitively.
Make IMAP LOGIN and AUTHENTICATE drive SASL exchanges, apply authenticated identities to IMAP sessions, support final server data, and keep SASL cleanup robust.
Build each Guice IMAP suite with server-specific SASL mechanisms and defaults, while keeping capability and enable processors scoped to the same suite.
…n wiring Stop carrying OIDC/authentication configuration in IMAP server/session objects now that SASL factories own mechanism configuration. Keep Spring IMAP startup compatible with static defaults.
Update the custom IMAP example to configure SASL factories through auth.saslMechanisms, remove extension module boilerplate, and demonstrate continuation plus final server data.
Hello, I did attempt to:
|
| * | ||
| * @param channelEncrypted whether the underlying transport is encrypted, for example with TLS. | ||
| */ | ||
| default boolean isAvailableOnTransport(boolean channelEncrypted) { |
There was a problem hiding this comment.
We can concider passin a raw ProtocolSession here I think if we want to be more generic
There was a problem hiding this comment.
ImapSession does not extend ProtocolSession, therefore ProtocolSession is not generic enough to be used here...
Unless we refactor ImapSession to extend ProtocolSession, which feels like another core refactoring.
I guess we can live with boolean channelEncrypted for now, and refactor it further later if needed.
| private final Authorizator authorizator; | ||
|
|
||
| @Inject | ||
| public JamesSaslAuthenticator(Authenticator authenticator, Authorizator authorizator) { |
There was a problem hiding this comment.
Or can't we rather user SessionProvider ?
How about letting the Sasl mechanism directly play with SessionProvider ? And we just inject SessionProvider within SaslMechanismFacoties ?
There was a problem hiding this comment.
IMO, we should avoid injecting SessionProvider directly into SASL mechanisms because SessionProvider returns MailboxSession, which is an IMAP/mailbox-specific session object.
For example, if SMTP later reuses OAUTHBEARER, the mechanism should not create a MailboxSession. SMTP only needs the authenticated/authorized identity to update its own SMTP session state and relay/submission policy.
That is why I prefer keeping a small SaslAuthenticator wrapper: mechanisms perform credential validation and authorization, then return a protocol-neutral SaslAuthenticationResult.Success(SaslIdentity). Each protocol remains responsible for initializing its own session model from that identity.
There was a problem hiding this comment.
I do not understand why we cannot unify this behaviour as well TBH
…edInUser information
OauthBearer and XOauth2 share 1 mechanism class. OauthBearer/XOauth2 factory will decide the name of mechanism.
|
I will plan SMTP adoption in another PR. We can wait to see whether SMTP adoption is well aligned and viable following the SPI introduced, before merging this IMAP work, if needed. But I presume SMTP adoption would be good. |
Yes please |
TODO: