@@ -53,7 +53,8 @@ type SMTPHandler struct {
5353cfg codersdk.NotificationsEmailConfig
5454log slog.Logger
5555
56- loginWarnOnce sync.Once
56+ noAuthWarnOnce sync.Once
57+ loginWarnOnce sync.Once
5758
5859helpers template.FuncMap
5960}
@@ -136,14 +137,20 @@ func (s *SMTPHandler) dispatch(subject, htmlBody, plainBody, to string) Delivery
136137
137138// Check for authentication capabilities.
138139if ok ,avail := c .Extension ("AUTH" );ok {
139- // Ensure the auth mechanisms available are ones we can use.
140+ // Ensure the auth mechanisms available are ones we can use, and create a SASL client .
140141auth ,err := s .auth (ctx ,avail )
141142if err != nil {
142143return true ,xerrors .Errorf ("determine auth mechanism: %w" ,err )
143144}
144145
145- // If so, use the auth mechanism to authenticate.
146- if auth != nil {
146+ if auth == nil {
147+ // If we get here, no SASL client (which handles authentication) was returned.
148+ // This is expected if auth is supported by the smarthost BUT no authentication details were configured.
149+ s .noAuthWarnOnce .Do (func () {
150+ s .log .Warn (ctx ,"skipping auth; no authentication client created" )
151+ })
152+ }else {
153+ // We have a SASL client, use it to authenticate.
147154if err := c .Auth (auth );err != nil {
148155return true ,xerrors .Errorf ("%T auth: %w" ,auth ,err )
149156}
@@ -431,6 +438,12 @@ func (s *SMTPHandler) loadCertificate() (*tls.Certificate, error) {
431438func (s * SMTPHandler )auth (ctx context.Context ,mechs string ) (sasl.Client ,error ) {
432439username := s .cfg .Auth .Username .String ()
433440
441+ // All auth mechanisms require username, so if one is not defined then don't return an auth client.
442+ if username == "" {
443+ // nolint:nilnil // This is a valid response.
444+ return nil ,nil
445+ }
446+
434447var errs error
435448list := strings .Split (mechs ," " )
436449for _ ,mech := range list {