diff --git a/config/config.go b/config/config.go index 20ba274..276bdfb 100644 --- a/config/config.go +++ b/config/config.go @@ -25,7 +25,9 @@ type Config struct { } HTTP struct { - Port string + Port string + AllowedOrigins []string + AllowFirefoxExtensions bool } Environment constants.Environment @@ -74,12 +76,14 @@ func load() Config { v.SetDefault("Database.PrivateDBName", "private") v.SetDefault("Database.PublicDBName", "public") v.SetDefault("HTTP.Port", "80") + v.SetDefault("HTTP.AllowFirefoxExtensions", false) v.SetDefault("Environment", constants.EnvironmentDevelopment) v.SetDefault("TrustProxy", false) v.SetDefault("Ingestors.CSFloat.Enable", false) v.SetDefault("Ingestors.CSFloat.BaseURL", "https://csfloat.com") // Need to register environment variables if defaults aren't set + v.BindEnv("HTTP.AllowedOrigins") v.BindEnv("Ingestors.CSFloat.SecretKey") // Try to find the root directory, but don't panic if it fails since go.mod doesn't exist in production diff --git a/go.mod b/go.mod index 09b7aa8..c236b46 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ toolchain go1.24.7 require ( github.com/go-chi/chi/v5 v5.2.4 + github.com/go-chi/cors v1.2.2 github.com/go-chi/render v1.0.3 github.com/go-viper/mapstructure/v2 v2.4.0 github.com/google/go-cmp v0.7.0 diff --git a/go.sum b/go.sum index bdf6158..ae82bf0 100644 --- a/go.sum +++ b/go.sum @@ -9,6 +9,8 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/go-chi/chi/v5 v5.2.4 h1:WtFKPHwlywe8Srng8j2BhOD9312j9cGUxG1SP4V2cR4= github.com/go-chi/chi/v5 v5.2.4/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0= +github.com/go-chi/cors v1.2.2 h1:Jmey33TE+b+rB7fT8MUy1u0I4L+NARQlK6LhzKPSyQE= +github.com/go-chi/cors v1.2.2/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4= github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= diff --git a/server/server.go b/server/server.go index 80e9cd5..5f5a85b 100644 --- a/server/server.go +++ b/server/server.go @@ -2,6 +2,8 @@ package server import ( "net/http" + "regexp" + "strings" "reverse-watch/api" "reverse-watch/config" @@ -10,6 +12,7 @@ import ( "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" + "github.com/go-chi/cors" ) type Server struct { @@ -19,6 +22,33 @@ type Server struct { func New(cfg config.Config, factory repository.Factory) (*Server, error) { r := chi.NewRouter() + firefoxExtensionOrigin := regexp.MustCompile("^moz-extension://[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$") + + r.Use(cors.Handler(cors.Options{ + AllowOriginFunc: func(r *http.Request, origin string) bool { + for _, allowedOrigin := range cfg.HTTP.AllowedOrigins { + if allowedOrigin == origin { + return true + } + } + + if cfg.HTTP.AllowFirefoxExtensions { + // Firefox extension IDs are randomly generated for each user. + // Therefore, we're scoping requests made from Firefox extensions to specific endpoints only. + if firefoxExtensionOrigin.MatchString(origin) { + if strings.HasPrefix(r.RequestURI, "/api/v1/users/") { + return true + } + } + } + return false + }, + AllowedMethods: []string{"GET", "OPTIONS"}, + AllowedHeaders: []string{"Accept", "Content-Type"}, + AllowCredentials: true, + MaxAge: 300, + })) + r.Use(middleware.Recoverer) r.Use(middleware.RequestID) r.Use(middleware.RealIP)