all repos — homestead @ c0f110119e434f188f5959c48570df12121cc663

Code for my website

extract webfinger and oidc code

Alan Pearce
commit

c0f110119e434f188f5959c48570df12121cc663

parent

4fe285fc78fdadde99c439e3691670b009087655

A domain/identity/oidc/service.go
@@ -0,0 +1,45 @@
+package oidc + +import ( + "net/http" + + "alin.ovh/homestead/shared/config" + sharedhttp "alin.ovh/homestead/shared/http" + "alin.ovh/x/log" + + "github.com/benpate/digit" +) + +type Service struct { + config *config.Config + log *log.Logger + acctResource string + resource digit.Resource +} + +func New(cfg *config.Config, logger *log.Logger) *Service { + acctResource := "acct:" + cfg.OIDCEmail + resource := digit.NewResource(acctResource). + Link("http://openid.net/specs/connect/1.0/issuer", "", cfg.OIDCHost.String()) + + return &Service{ + config: cfg, + log: logger, + acctResource: acctResource, + resource: resource, + } +} + +func (s *Service) RegisterHandlers(mux *sharedhttp.ServeMux) { + const oidcPath = "/.well-known/openid-configuration" + mux.ServeMux.Handle(oidcPath, + sharedhttp.RedirectHandler(s.config.OIDCHost.JoinPath(oidcPath), http.StatusFound)) +} + +func (s *Service) GetResource() string { + return s.acctResource +} + +func (s *Service) GetIdentityResource() digit.Resource { + return s.resource +}
A domain/identity/service.go
@@ -0,0 +1,44 @@
+package identity + +import ( + "alin.ovh/homestead/domain/identity/oidc" + "alin.ovh/homestead/domain/identity/webfinger" + "alin.ovh/homestead/shared/config" + "alin.ovh/homestead/shared/http" + "alin.ovh/x/log" +) + +type Service struct { + oidc *oidc.Service + webfinger *webfinger.Service + log *log.Logger +} + +func New(cfg *config.Config, logger *log.Logger) *Service { + oidcService := oidc.New(cfg, logger.Named("oidc")) + + webfingerService := webfinger.New( + logger.Named("webfinger"), + []webfinger.ResourceProvider{oidcService}, + ) + + return &Service{ + oidc: oidcService, + webfinger: webfingerService, + log: logger, + } +} + +func (s *Service) RegisterHandlers(mux *http.ServeMux) { + s.oidc.RegisterHandlers(mux) + + mux.HandleFunc("/.well-known/webfinger", s.webfinger.HandleFunc) +} + +func (s *Service) GetOIDCService() *oidc.Service { + return s.oidc +} + +func (s *Service) GetWebFingerService() *webfinger.Service { + return s.webfinger +}
A domain/identity/webfinger/service.go
@@ -0,0 +1,87 @@
+package webfinger + +import ( + "encoding/json" + "net/http" + + ihttp "alin.ovh/homestead/shared/http" + "alin.ovh/x/log" + + "github.com/benpate/digit" +) + +type ResourceProvider interface { + GetResource() string + GetIdentityResource() digit.Resource +} + +type Service struct { + log *log.Logger + providers []ResourceProvider + corsOrigin string +} + +type Option func(*Service) + +func WithCORSOrigin(origin string) Option { + return func(s *Service) { + s.corsOrigin = origin + } +} + +func New(logger *log.Logger, providers []ResourceProvider, opts ...Option) *Service { + service := &Service{ + log: logger, + providers: providers, + corsOrigin: "*", // Default to allow all origins + } + + for _, opt := range opts { + opt(service) + } + + return service +} + +func (s *Service) RegisterHandlers(mux *http.ServeMux) { + mux.HandleFunc("/.well-known/webfinger", s.handleWebFinger) +} + +func (s *Service) Handler() http.HandlerFunc { + return s.handleWebFinger +} + +func (s *Service) HandleFunc(w http.ResponseWriter, r *http.Request) *ihttp.Error { + resource := r.URL.Query().Get("resource") + if resource == "" { + return ihttp.BadRequest("Missing resource parameter", nil) + } + + for _, provider := range s.providers { + if resource == provider.GetResource() { + w.Header().Add("Content-Type", "application/jrd+json") + + if s.corsOrigin != "" { + w.Header().Add("Access-Control-Allow-Origin", s.corsOrigin) + } + + if err := json.NewEncoder(w).Encode(provider.GetIdentityResource()); err != nil { + return ihttp.InternalServerError("Failed to encode webfinger response", err) + } + + return nil + } + } + + return ihttp.NotFound("Resource not found") +} + +func (s *Service) handleWebFinger(w http.ResponseWriter, r *http.Request) { + if err := s.HandleFunc(w, r); err != nil { + status := err.Code + if status == 0 { + status = http.StatusInternalServerError + } + http.Error(w, err.Error(), status) + } +}
M domain/web/mux.godomain/web/mux.go
@@ -1,7 +1,6 @@
package website import ( - "encoding/json" "fmt" "net/http" "regexp"
@@ -15,20 +14,6 @@ ihttp "alin.ovh/homestead/shared/http"
"github.com/kevinpollet/nego" ) - -func (website *Website) webfinger(w http.ResponseWriter, r *http.Request) *ihttp.Error { - if r.URL.Query().Get("resource") == website.acctResource { - w.Header().Add("Content-Type", "application/jrd+json") - w.Header().Add("Access-Control-Allow-Origin", "*") - if err := json.NewEncoder(w).Encode(website.me); err != nil { - return ihttp.InternalServerError("Failed to encode webfinger response", err) - } - - return nil - } - - return ihttp.NotFound("Resource not found") -} func (website *Website) ErrorHandler(err *ihttp.Error, w http.ResponseWriter, r *http.Request) { if strings.Contains(r.Header.Get("Accept"), "text/html") {
@@ -110,7 +95,7 @@ }
func (website *Website) MakeRedirectorApp() *server.App { mux := ihttp.NewServeMux() - mux.HandleFunc("/.well-known/webfinger", website.webfinger) + website.identity.RegisterHandlers(mux) re := regexp.MustCompile( "^(.*)\\." + strings.ReplaceAll(website.config.WildcardDomain, ".", `\.`) + "$",
M domain/web/website.godomain/web/website.go
@@ -14,6 +14,7 @@ "alin.ovh/homestead/domain/analytics/goatcounter"
"alin.ovh/homestead/domain/analytics/nullcounter" "alin.ovh/homestead/domain/calendar" "alin.ovh/homestead/domain/content/fetcher" + "alin.ovh/homestead/domain/identity" "alin.ovh/homestead/domain/web/server" "alin.ovh/homestead/domain/web/templates" "alin.ovh/homestead/shared/config"
@@ -26,7 +27,6 @@ "alin.ovh/x/log"
"github.com/Southclaws/fault" "github.com/Southclaws/fault/fmsg" - "github.com/benpate/digit" "github.com/crewjam/csp" "github.com/osdevisnot/sorvor/pkg/livereload" )
@@ -51,8 +51,7 @@ counter stats.Counter
log *log.Logger reader storage.Reader calendar *calendar.Calendar - me digit.Resource - acctResource string + identity *identity.Service CSP *csp.Header *server.App }
@@ -200,17 +199,13 @@ }()
<-firstUpdate - website.acctResource = "acct:" + cfg.OIDCEmail - website.me = digit.NewResource(website.acctResource). - Link("http://openid.net/specs/connect/1.0/issuer", "", cfg.OIDCHost.String()) + website.identity = identity.New(cfg, log.Named("identity")) mux := ihttp.NewServeMux() mux.HandleError(website.ErrorHandler) mux.Handle("/", website) mux.HandleFunc("/calendar", website.Calendar) - mux.HandleFunc("/.well-known/webfinger", website.webfinger) - const oidcPath = "/.well-known/openid-configuration" - mux.ServeMux.Handle(oidcPath, ihttp.RedirectHandler(cfg.OIDCHost.JoinPath(oidcPath), 302)) + website.identity.RegisterHandlers(mux) if opts.Development { staticHandler := func(w http.ResponseWriter, r *http.Request) *ihttp.Error {