can oauth
This commit is contained in:
85
email.go
85
email.go
@@ -1,17 +1,26 @@
|
||||
package contact
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/mail"
|
||||
"net/smtp"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/bytbox/go-pop3"
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-imap/client"
|
||||
"github.com/emersion/go-sasl"
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/oauth2/authhandler"
|
||||
"golang.org/x/oauth2/google"
|
||||
)
|
||||
|
||||
type Emailer struct {
|
||||
@@ -21,6 +30,7 @@ type Emailer struct {
|
||||
IMAP string
|
||||
Password string
|
||||
Limit int
|
||||
OAuth string
|
||||
}
|
||||
|
||||
func NewEmailer() *Emailer {
|
||||
@@ -34,6 +44,7 @@ func NewEmailer() *Emailer {
|
||||
From: envOr("FROM", "breellocaldev@gmail.com"),
|
||||
SMTP: envOr("SMTP", "smtp.gmail.com:465"),
|
||||
Password: envOr("PASSWORD", "lhnjijrvqaesiufp"),
|
||||
OAuth: envOr("OAUTH", ""),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,12 +53,82 @@ func (e *Emailer) Read() (chan *mail.Message, error) {
|
||||
}
|
||||
|
||||
func (e *Emailer) ReadIMAP() (chan *mail.Message, error) {
|
||||
if e.OAuth != "" {
|
||||
return e.oauthThenReadIMAP()
|
||||
}
|
||||
return e.readIMAP(e.From, e.Password)
|
||||
}
|
||||
|
||||
func (e *Emailer) oauthThenReadIMAP() (chan *mail.Message, error) {
|
||||
configJSON, err := e.configJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if token, _ := os.ReadFile(e.OAuth + ".token"); len(token) == 0 {
|
||||
state := uuid.NewString()
|
||||
|
||||
verifier := uuid.NewString()
|
||||
s256 := sha256.Sum256([]byte(verifier))
|
||||
challenge := base64.RawURLEncoding.EncodeToString(s256[:])
|
||||
pkceParams := authhandler.PKCEParams{Challenge: challenge, ChallengeMethod: "S256", Verifier: verifier}
|
||||
config, err := google.ConfigFromJSON([]byte(configJSON), "https://mail.google.com/")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
credentialsParams := google.CredentialsParams{Scopes: config.Scopes, State: state, AuthHandler: func(authCodeURL string) (string, string, error) {
|
||||
fmt.Println(authCodeURL + "&access_type=offline&prompt=consent")
|
||||
var callback string
|
||||
_, err := fmt.Scan(&callback)
|
||||
u, _ := url.Parse(callback)
|
||||
fmt.Println()
|
||||
return u.Query().Get("code"), state, err
|
||||
}, PKCE: &pkceParams}
|
||||
credentials, err := google.CredentialsFromJSONWithParams(context.Background(), []byte(configJSON), credentialsParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
token, err := credentials.TokenSource.Token()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tokenFileJSON, _ := json.Marshal(token)
|
||||
os.WriteFile(e.OAuth+".token", tokenFileJSON, os.ModePerm)
|
||||
log.Printf("%s", tokenFileJSON)
|
||||
}
|
||||
token, _ := os.ReadFile(e.OAuth + ".token")
|
||||
var tokenStruct struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
}
|
||||
json.Unmarshal(token, &tokenStruct)
|
||||
log.Println(e.From, tokenStruct.AccessToken)
|
||||
return e.readIMAP(e.From, tokenStruct.AccessToken)
|
||||
}
|
||||
|
||||
func (e *Emailer) configJSON() (string, error) {
|
||||
b, err := os.ReadFile(e.OAuth)
|
||||
return string(b), err
|
||||
}
|
||||
|
||||
func (e *Emailer) readIMAP(authU, authP string) (chan *mail.Message, error) {
|
||||
c, err := client.DialTLS(e.IMAP, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.Login(e.From, e.Password); err != nil {
|
||||
return nil, err
|
||||
if e.OAuth == "" {
|
||||
if err := c.Login(authU, authP); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if err := c.Authenticate(sasl.NewOAuthBearerClient(&sasl.OAuthBearerOptions{
|
||||
Username: authU,
|
||||
Token: authP,
|
||||
Host: "",
|
||||
Port: 0,
|
||||
})); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
mbox, err := c.Select("INBOX", true) //readonly
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user