can oauth

This commit is contained in:
bel
2026-02-23 22:59:40 -07:00
parent 14036702ee
commit 5a4ce70451
6 changed files with 108 additions and 5 deletions

View File

@@ -1,6 +1,6 @@
module gitea.inhome.blapointe.com/contact/cmd/recv
go 1.20
go 1.24.0
replace gitea.inhome.blapointe.com/local-sandbox/contact => ../../
@@ -10,9 +10,12 @@ require (
)
require (
cloud.google.com/go/compute/metadata v0.3.0 // indirect
github.com/bytbox/go-pop3 v0.0.0-20120201222208-3046caf0763e // indirect
github.com/emersion/go-imap v1.2.0 // indirect
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 // indirect
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 // indirect
github.com/google/uuid v1.6.0 // indirect
golang.org/x/oauth2 v0.35.0 // indirect
golang.org/x/text v0.3.7 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

View File

@@ -1,3 +1,5 @@
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
gitea.inhome.blapointe.com/local/args v0.0.0-20231109145953-eb2e1c1b8d56 h1:zTGGZ77KLFagqUvDSgTOnm0qF+iSLwQWiEtGjb2jjlY=
gitea.inhome.blapointe.com/local/args v0.0.0-20231109145953-eb2e1c1b8d56/go.mod h1:SqCOE3bE3wvrztVIQGHuyxHKfDjRKU9EWhBdkmkiwyc=
github.com/bytbox/go-pop3 v0.0.0-20120201222208-3046caf0763e h1:mQTN05gz0rDZSABqKMzAPMb5ATWcvvdMljRzEh0LjBo=
@@ -7,7 +9,13 @@ github.com/emersion/go-imap v1.2.0/go.mod h1:Qlx1FSx2FTxjnjWpIlVNEuX+ylerZQNFE5N
github.com/emersion/go-message v0.15.0/go.mod h1:wQUEfE+38+7EW8p8aZ96ptg6bAb1iwdgej19uXASlE4=
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 h1:OJyUGMJTzHTd1XQp98QTaHernxMYzRaOasRir9hUlFQ=
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 h1:oP4q0fw+fOSWn3DfFi4EXdT+B+gTtzx8GC9xsc26Znk=
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ=
golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=

View File

@@ -24,6 +24,7 @@ func main() {
as.Append(args.STRING, "p", "password", emailer.Password)
as.Append(args.INT, "b", "dont read more than this many characters", 4096)
as.Append(args.BOOL, "json", "output as json", false)
as.Append(args.STRING, "oauth", "", emailer.OAuth)
if err := as.Parse(); err != nil {
panic(err)
}
@@ -33,6 +34,7 @@ func main() {
emailer.Limit = as.Get("n").GetInt()
emailer.From = as.GetString("u")
emailer.Password = as.GetString("p")
emailer.OAuth = as.GetString("oauth")
var msgs <-chan *mail.Message
var err error

View File

@@ -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 {

5
go.mod
View File

@@ -1,13 +1,16 @@
module gitea.inhome.blapointe.com/local-sandbox/contact
go 1.17
go 1.24.0
require (
github.com/bytbox/go-pop3 v0.0.0-20120201222208-3046caf0763e
github.com/emersion/go-imap v1.2.0
github.com/google/uuid v1.6.0
golang.org/x/oauth2 v0.35.0
)
require (
cloud.google.com/go/compute/metadata v0.3.0 // indirect
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 // indirect
golang.org/x/text v0.3.7 // indirect
)

6
go.sum
View File

@@ -1,3 +1,5 @@
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
github.com/bytbox/go-pop3 v0.0.0-20120201222208-3046caf0763e h1:mQTN05gz0rDZSABqKMzAPMb5ATWcvvdMljRzEh0LjBo=
github.com/bytbox/go-pop3 v0.0.0-20120201222208-3046caf0763e/go.mod h1:alXX+s7a4cKaIprgjeEboqi4Tm7XR/HXEwUTxUV/ywU=
github.com/emersion/go-imap v1.2.0 h1:lyUQ3+EVM21/qbWE/4Ya5UG9r5+usDxlg4yfp3TgHFA=
@@ -6,6 +8,10 @@ github.com/emersion/go-message v0.15.0/go.mod h1:wQUEfE+38+7EW8p8aZ96ptg6bAb1iwd
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 h1:OJyUGMJTzHTd1XQp98QTaHernxMYzRaOasRir9hUlFQ=
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ=
golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=