Skip to content

Instantly share code, notes, and snippets.

@andelf
Created March 8, 2013 18:40
Show Gist options
  • Select an option

  • Save andelf/5118732 to your computer and use it in GitHub Desktop.

Select an option

Save andelf/5118732 to your computer and use it in GitHub Desktop.
golang net/smtp SMTP AUTH LOGIN Auth Handler
// MIT license (c) andelf 2013
import (
"net/smtp"
"errors"
)
type loginAuth struct {
username, password string
}
func LoginAuth(username, password string) smtp.Auth {
return &loginAuth{username, password}
}
func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
return "LOGIN", []byte{}, nil
}
func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
if more {
switch string(fromServer) {
case "Username:":
return []byte(a.username), nil
case "Password:":
return []byte(a.password), nil
default:
return nil, errors.New("Unkown fromServer")
}
}
return nil, nil
}
// usage:
// auth := LoginAuth("loginname", "password")
// err := smtp.SendMail(smtpServer + ":25", auth, fromAddress, toAddresses, []byte(message))
// or
// client, err := smtp.Dial(smtpServer)
// client.Auth(LoginAuth("loginname", "password"))
@homme
Copy link
Copy Markdown

homme commented Feb 16, 2015

Very useful, thanks! I've updated the code to submit the username when Start is called, which means one less network exchange.

@alejandrodbm
Copy link
Copy Markdown

Extremely grateful to see that this was the answer to all my problems.

Thank you very very much...!

@danielledeleo
Copy link
Copy Markdown

Such a simple fix. Can't believe the standard library doesn't support LOGIN.

@tmack8080
Copy link
Copy Markdown

This worked for me using:
Server: smtp-mail.outlook.com
Port: 587
SMTP security: STARTTLS

Thank you for posting.

@andboson
Copy link
Copy Markdown

thx dude!

@MaoJianwei
Copy link
Copy Markdown

this works! many thanks!

@FeranyDev
Copy link
Copy Markdown

Oh, it's so beautiful.

@AzraelJi
Copy link
Copy Markdown

666

@three-ball
Copy link
Copy Markdown

Very useful, Thanks for your code, this works for me!

@karlovskiy
Copy link
Copy Markdown

Also, there is login auth support in go-mail package https://github.com/go-gomail/gomail/blob/master/auth.go

@hamdismaoui
Copy link
Copy Markdown

Thank you so much!
Solution still working fine.

@jypelle
Copy link
Copy Markdown

jypelle commented Jan 10, 2024

And if you don't want to choose between PLAIN and LOGIN:

func PlainOrLoginAuth(username, password, host string) smtp.Auth {
	return &plainOrLoginAuth{username: username, password: password, host: host}
}

func isLocalhost(name string) bool {
	return name == "localhost" || name == "127.0.0.1" || name == "::1"
}

type plainOrLoginAuth struct {
	username   string
	password   string
	host       string
	authMethod string
}

func (a *plainOrLoginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
	// Must have TLS, or else localhost server.
	// Note: If TLS is not true, then we can't trust ANYTHING in ServerInfo.
	// In particular, it doesn't matter if the server advertises PLAIN auth.
	// That might just be the attacker saying
	// "it's ok, you can trust me with your password."
	if !server.TLS && !isLocalhost(server.Name) {
		return "", nil, errors.New("unencrypted connection")
	}
	if server.Name != a.host {
		return "", nil, errors.New("wrong host name")
	}
	if !slices.Contains(server.Auth, "PLAIN") {
		a.authMethod = "LOGIN"
		return a.authMethod, nil, nil
	} else {
		a.authMethod = "PLAIN"
		resp := []byte("\x00" + a.username + "\x00" + a.password)
		return a.authMethod, resp, nil
	}
}

func (a *plainOrLoginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
	if !more {
		return nil, nil
	}

	if a.authMethod == "PLAIN" {
		// We've already sent everything.
		return nil, errors.New("unexpected server challenge")
	}

	switch {
	case bytes.Equal(fromServer, []byte("Username:")):
		return []byte(a.username), nil
	case bytes.Equal(fromServer, []byte("Password:")):
		return []byte(a.password), nil
	default:
		return nil, fmt.Errorf("unexpected server challenge: %s", fromServer)
	}
}

@jrichardsz
Copy link
Copy Markdown

Thanks a lot!!!

@nicmwe
Copy link
Copy Markdown

nicmwe commented May 15, 2024

Thank you for this!

@AmrMady
Copy link
Copy Markdown

AmrMady commented Dec 3, 2024

Thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment