about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorR Chowdhury <necaris@gmail.com>2020-05-26 07:29:58 -0400
committerReto Brunner <reto@labrat.space>2020-08-13 07:44:43 +0200
commitf4dc7e1f746582d42462ec56347dd354756203b0 (patch)
treed4535b0aeecd9d323f12fcf1e3040babdbc79da1
parentf1a0fd20d6d30fb1203ffa3cb9f4004476acfdb3 (diff)
downloadaerc-f4dc7e1f746582d42462ec56347dd354756203b0.tar.gz
Add `oauthbearer` support for SMTP
This piggybacks on the existing IMAP support, and uses the same
configuration format (my local testing example has the IMAP and SMTP
lines almost copy-pasted from one another).

It's a little clumsy in that a new token is negotiated for every
`Send()` command, but it's a start...
-rw-r--r--commands/compose/send.go31
-rw-r--r--lib/oauthbearer.go4
2 files changed, 33 insertions, 2 deletions
diff --git a/commands/compose/send.go b/commands/compose/send.go
index 40ac4ca..dd7ece9 100644
--- a/commands/compose/send.go
+++ b/commands/compose/send.go
@@ -16,9 +16,11 @@ import (
 	"github.com/miolini/datacounter"
 	"github.com/pkg/errors"
 
+	"git.sr.ht/~sircmpwn/aerc/lib"
 	"git.sr.ht/~sircmpwn/aerc/models"
 	"git.sr.ht/~sircmpwn/aerc/widgets"
 	"git.sr.ht/~sircmpwn/aerc/worker/types"
+	"golang.org/x/oauth2"
 )
 
 type Send struct{}
@@ -97,6 +99,35 @@ func (Send) Execute(aerc *widgets.Aerc, args []string) error {
 	case "plain":
 		password, _ := uri.User.Password()
 		saslClient = sasl.NewPlainClient("", uri.User.Username(), password)
+	case "oauthbearer":
+		q := uri.Query()
+
+		oauth2 := &oauth2.Config{}
+		if q.Get("token_endpoint") != "" {
+			oauth2.ClientID = q.Get("client_id")
+			oauth2.ClientSecret = q.Get("client_secret")
+			oauth2.Scopes = []string{q.Get("scope")}
+			oauth2.Endpoint.TokenURL = q.Get("token_endpoint")
+		}
+
+		password, _ := uri.User.Password()
+		bearer := lib.OAuthBearer{
+			OAuth2:  oauth2,
+			Enabled: true,
+		}
+		if bearer.OAuth2.Endpoint.TokenURL == "" {
+			return fmt.Errorf("No 'TokenURL' configured for this account")
+		}
+		token, err := bearer.ExchangeRefreshToken(password)
+		if err != nil {
+			return err
+		}
+		password = token.AccessToken
+
+		saslClient = sasl.NewOAuthBearerClient(&sasl.OAuthBearerOptions{
+			Username: uri.User.Username(),
+			Token:    password,
+		})
 	default:
 		return fmt.Errorf("Unsupported auth mechanism %s", auth)
 	}
diff --git a/lib/oauthbearer.go b/lib/oauthbearer.go
index 5bcba60..1030696 100644
--- a/lib/oauthbearer.go
+++ b/lib/oauthbearer.go
@@ -13,7 +13,7 @@ type OAuthBearer struct {
 	Enabled bool
 }
 
-func (c *OAuthBearer) exchangeRefreshToken(refreshToken string) (*oauth2.Token, error) {
+func (c *OAuthBearer) ExchangeRefreshToken(refreshToken string) (*oauth2.Token, error) {
 	token := new(oauth2.Token)
 	token.RefreshToken = refreshToken
 	token.TokenType = "Bearer"
@@ -26,7 +26,7 @@ func (c *OAuthBearer) Authenticate(username string, password string, client *cli
 	}
 
 	if c.OAuth2.Endpoint.TokenURL != "" {
-		token, err := c.exchangeRefreshToken(password)
+		token, err := c.ExchangeRefreshToken(password)
 		if err != nil {
 			return err
 		}