Add OAuth 2.0 backend

This commit is contained in:
Simon Ser 2024-02-19 17:19:32 +00:00 committed by Conrad Hoffmann
parent cca1d579db
commit ebb5aede92
5 changed files with 105 additions and 0 deletions

87
auth/oauth2.go Normal file
View File

@ -0,0 +1,87 @@
package auth
import (
"context"
"fmt"
"net/http"
"strings"
"github.com/rs/zerolog/log"
"git.sr.ht/~emersion/go-oauth2"
)
type OAuth2Provider struct {
metadata *oauth2.ServerMetadata
clientID string
clientSecret string
}
// Initializes a new OAuth 2.0 auth provider with the given connection string.
func NewOAuth2(endpoint, clientID, clientSecret string) (AuthProvider, error) {
metadata, err := oauth2.DiscoverServerMetadata(context.Background(), endpoint)
if err != nil {
return nil, fmt.Errorf("failed to fetch OAuth 2.0 server metadata: %v", err)
}
return &OAuth2Provider{
metadata: metadata,
clientID: clientID,
clientSecret: clientSecret,
}, nil
}
func (prov *OAuth2Provider) 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 *OAuth2Provider) doAuth(next http.Handler,
w http.ResponseWriter, r *http.Request) {
auth := r.Header.Get("Authorization")
authScheme, creds, _ := strings.Cut(auth, " ")
var username, accessToken string
switch strings.ToLower(authScheme) {
case "bearer":
accessToken = creds
case "basic":
username, accessToken, _ = r.BasicAuth()
default:
w.Header().Add("WWW-Authenticate", `Bearer, Basic realm="Please provide an OAuth access token", charset="UTF-8"`)
http.Error(w, "HTTP auth is required", http.StatusUnauthorized)
return
}
client := oauth2.Client{
Server: prov.metadata,
ClientID: prov.clientID,
ClientSecret: prov.clientSecret,
}
resp, err := client.Introspect(r.Context(), accessToken)
if err != nil || !resp.Active {
log.Debug().Err(err).Msg("auth error")
http.Error(w, "Invalid access token", http.StatusUnauthorized)
return
}
if username != "" && username != resp.Username {
http.Error(w, "Invalid username", http.StatusUnauthorized)
return
}
if resp.Username == "" {
http.Error(w, "OAuth 2.0 server did not send username", http.StatusInternalServerError)
return
}
authCtx := AuthContext{
AuthMethod: "oauth2",
UserName: resp.Username,
}
ctx := NewContext(r.Context(), &authCtx)
r = r.WithContext(ctx)
next.ServeHTTP(w, r)
}

View File

@ -26,6 +26,14 @@ func NewFromURL(authURL string) (AuthProvider, error) {
return NewHtpasswd(path) return NewHtpasswd(path)
case "null": case "null":
return NewNull() return NewNull()
case "http", "https":
if u.User == nil {
return nil, fmt.Errorf("missing client ID for OAuth 2.0")
}
clientID := u.User.Username()
clientSecret, _ := u.User.Password()
u.User = nil
return NewOAuth2(u.String(), clientID, clientSecret)
default: default:
return nil, fmt.Errorf("no auth provider found for %s:// URL", u.Scheme) return nil, fmt.Errorf("no auth provider found for %s:// URL", u.Scheme)
} }

View File

@ -74,6 +74,13 @@ URL: *pam://* (no parameters)
_Note:_ The PAM auth backend must be enabled at build time, as PAM may not be _Note:_ The PAM auth backend must be enabled at build time, as PAM may not be
available on all platforms. available on all platforms.
## OAuth 2.0
The OAuth 2.0 auth backend delegates authentication to the provided OAuth 2.0
server.
URL: *https://*_client_id_*:*_client_secret_*@*_host_
## Static file (htpasswd) ## Static file (htpasswd)
The static file auth backend relies on the file format popularized by Apache and The static file auth backend relies on the file format popularized by Apache and

1
go.mod
View File

@ -3,6 +3,7 @@ module git.sr.ht/~sircmpwn/tokidoki
go 1.18 go 1.18
require ( require (
git.sr.ht/~emersion/go-oauth2 v0.0.0-20240217160856-2e0d6e20b088
github.com/emersion/go-ical v0.0.0-20220601085725-0864dccc089f github.com/emersion/go-ical v0.0.0-20220601085725-0864dccc089f
github.com/emersion/go-imap v1.2.1 github.com/emersion/go-imap v1.2.1
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43

2
go.sum
View File

@ -1,3 +1,5 @@
git.sr.ht/~emersion/go-oauth2 v0.0.0-20240217160856-2e0d6e20b088 h1:KuPliLD8CQM1WbCHdjHR6mhadIzLaAJCNENmvB1y9gs=
git.sr.ht/~emersion/go-oauth2 v0.0.0-20240217160856-2e0d6e20b088/go.mod h1:VHj0jSCLIkrfEwmOvJ4+ykpoVbD/YLN7BM523oKKBHc=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/emersion/go-ical v0.0.0-20220601085725-0864dccc089f h1:feGUUxxvOtWVOhTko8Cbmp33a+tU0IMZxMEmnkoAISQ= github.com/emersion/go-ical v0.0.0-20220601085725-0864dccc089f h1:feGUUxxvOtWVOhTko8Cbmp33a+tU0IMZxMEmnkoAISQ=
github.com/emersion/go-ical v0.0.0-20220601085725-0864dccc089f/go.mod h1:2MKFUgfNMULRxqZkadG1Vh44we3y5gJAtTBlVsx1BKQ= github.com/emersion/go-ical v0.0.0-20220601085725-0864dccc089f/go.mod h1:2MKFUgfNMULRxqZkadG1Vh44we3y5gJAtTBlVsx1BKQ=