Implement IMAP auth provider

This commit is contained in:
Drew DeVault 2022-02-21 10:55:02 +01:00
parent 96ef5f9b2a
commit 5a641ceca1
7 changed files with 100 additions and 1 deletions

View File

@ -0,0 +1,66 @@
package auth
import (
"log"
"net/http"
"github.com/emersion/go-imap/client"
"github.com/emersion/go-sasl"
)
type IMAPProvider struct {
addr string
tls bool
}
// Initializes a new IMAP auth provider with the given connection string.
func NewIMAP(addr string, tls bool) AuthProvider {
prov := &IMAPProvider{addr, tls}
conn, err := prov.dial()
if err != nil {
log.Fatalf("Error dialing configured IMAP auth server: %s", err.Error())
}
conn.Close()
return prov
}
func (prov *IMAPProvider) Middleware() func(http.Handler) http.Handler {
return func (next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
prov.doAuth(next, w, r)
})
}
}
func (prov *IMAPProvider) doAuth(next http.Handler,
w http.ResponseWriter, r *http.Request) {
user, pass, ok := r.BasicAuth()
if !ok {
w.Header().Add("WWW-Authenticate", `Basic realm="Please provide your IMAP credentials", charset="UTF-8"`)
http.Error(w, "HTTP Basic auth is required", http.StatusUnauthorized)
return
}
conn, err := prov.dial()
if err != nil {
http.Error(w, "Temporary authentication error, try again later", http.StatusServiceUnavailable)
return
}
defer conn.Close()
auth := sasl.NewPlainClient("", user, pass)
if err := conn.Authenticate(auth); err != nil {
http.Error(w, "Invalid username or password", http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r)
}
func (prov *IMAPProvider) dial() (*client.Client, error) {
if prov.tls {
return client.DialTLS(prov.addr, nil)
} else {
return client.Dial(prov.addr)
}
}

View File

@ -0,0 +1,11 @@
package auth
import (
"net/http"
)
// Abstracts the authentication backend for the server.
type AuthProvider interface {
// Returns HTTP middleware for performing authentication.
Middleware() func(http.Handler) http.Handler
}

View File

View File

View File

@ -8,6 +8,8 @@ import (
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware" "github.com/go-chi/chi/v5/middleware"
"git.sr.ht/~sircmpwn/tokidoki/auth"
) )
func main() { func main() {
@ -25,6 +27,10 @@ func main() {
mux := chi.NewRouter() mux := chi.NewRouter()
mux.Use(middleware.Logger) mux.Use(middleware.Logger)
// TODO: Configurable
authProvider := auth.NewIMAP("imap.migadu.com:993", true)
mux.Use(authProvider.Middleware())
mux.Get("/", func (w http.ResponseWriter, r *http.Request) { mux.Get("/", func (w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello world!\n")) w.Write([]byte("Hello world!\n"))
}) })

7
go.mod
View File

@ -2,4 +2,9 @@ module git.sr.ht/~sircmpwn/tokidoki
go 1.17 go 1.17
require github.com/go-chi/chi/v5 v5.0.7 // indirect require (
github.com/emersion/go-imap v1.2.0 // indirect
github.com/emersion/go-sasl v0.0.0-20211008083017-0b9dcfb154ac // indirect
github.com/go-chi/chi/v5 v5.0.7 // indirect
golang.org/x/text v0.3.7 // indirect
)

11
go.sum
View File

@ -1,2 +1,13 @@
github.com/emersion/go-imap v1.2.0 h1:lyUQ3+EVM21/qbWE/4Ya5UG9r5+usDxlg4yfp3TgHFA=
github.com/emersion/go-imap v1.2.0/go.mod h1:Qlx1FSx2FTxjnjWpIlVNEuX+ylerZQNFE5NsmKFSejY=
github.com/emersion/go-message v0.15.0/go.mod h1:wQUEfE+38+7EW8p8aZ96ptg6bAb1iwdgej19uXASlE4=
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
github.com/emersion/go-sasl v0.0.0-20211008083017-0b9dcfb154ac h1:tn/OQ2PmwQ0XFVgAHfjlLyqMewry25Rz7jWnVoh4Ggs=
github.com/emersion/go-sasl v0.0.0-20211008083017-0b9dcfb154ac/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8= github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8=
github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
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=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=