From b0eaf5191c9bc5b128e347625b7eef998ba63c41 Mon Sep 17 00:00:00 2001 From: Frode Aannevik Date: Wed, 10 Jul 2019 21:00:28 +0200 Subject: Support imaps with oauthbearer authentication (Gmail) imaps+oauthbearer://user:token@host?token_endpoint=... - the config Source password is used as access token if no token_endpoint parameter is set - the config Source password is used as refresh token if token_endpoint parameter is set, and used to exchange with an access token The implementation has only been tested with Gmail. source = imaps+oauthbearer://{username}:{refersh_token}@imap.gmail.com:993? \ client_id=XX&\ client_secret=XX&\ token_endpoint=https%3A%2F%2Faccounts.google.com%2Fo%2Foauth2%2Ftoken client credentials created with https://console.developers.google.com/apis/credentials refresh token created with https://github.com/google/gmail-oauth2-tools/blob/master/python/oauth2.py rel: https://todo.sr.ht/~sircmpwn/aerc2/42 --- lib/oauthbearer.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 lib/oauthbearer.go (limited to 'lib/oauthbearer.go') diff --git a/lib/oauthbearer.go b/lib/oauthbearer.go new file mode 100644 index 0000000..5bcba60 --- /dev/null +++ b/lib/oauthbearer.go @@ -0,0 +1,42 @@ +package lib + +import ( + "context" + "fmt" + "github.com/emersion/go-imap/client" + "github.com/emersion/go-sasl" + "golang.org/x/oauth2" +) + +type OAuthBearer struct { + OAuth2 *oauth2.Config + Enabled bool +} + +func (c *OAuthBearer) exchangeRefreshToken(refreshToken string) (*oauth2.Token, error) { + token := new(oauth2.Token) + token.RefreshToken = refreshToken + token.TokenType = "Bearer" + return c.OAuth2.TokenSource(context.TODO(), token).Token() +} + +func (c *OAuthBearer) Authenticate(username string, password string, client *client.Client) error { + if ok, err := client.SupportAuth(sasl.OAuthBearer); err != nil || !ok { + return fmt.Errorf("OAuthBearer not supported %v", err) + } + + if c.OAuth2.Endpoint.TokenURL != "" { + token, err := c.exchangeRefreshToken(password) + if err != nil { + return err + } + password = token.AccessToken + } + + saslClient := sasl.NewOAuthBearerClient(&sasl.OAuthBearerOptions{ + Username: username, + Token: password, + }) + + return client.Authenticate(saslClient) +} -- cgit 1.4.1-2-gfad0