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) var ( ErrMissingResourceParameter = ihttp.NewError( "Missing resource parameter", http.StatusBadRequest, ) ErrFailedToEncodeResponse = ihttp.NewError( "Failed to encode webfinger response", http.StatusInternalServerError, ) ErrNotFound = ihttp.NewError("Resource not found", http.StatusNotFound) ) 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 *ihttp.ServeMux) { mux.HandleFunc("/.well-known/webfinger", s.HandleFunc) } func (s *Service) HandleFunc(w http.ResponseWriter, r *http.Request) ihttp.Error { resource := r.URL.Query().Get("resource") if resource == "" { return ErrMissingResourceParameter } 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 ErrFailedToEncodeResponse.WithCause(err) } return nil } } return ErrNotFound }