Reputation: 33
I am building a web app in Golang, using mux as a router and Negroni for managing middleware for the API.
I have the following middleware:
I want to apply the middleware to specific routes as appropriate, i.e using the CORS middleware on all requests and checking auth only on the protected routes.
What is actually happens for me when I try to make API requests using the frontend is that the CORS middleware seems only to get called for an initial OPTIONS request for each API route and is not subsequently used.
For example:
We also see strange things on some of the other routes - i.e when making Postman GET requests to /api/entities we are able to get the list of entities (which should be protected) when not logged in. It is as if the authentication middleware is not being called or is not behaving as expected.
I am fairly new to some of these concepts so perhaps I have misunderstood something. Any help would be appreciated.
My code is as follows (main.go):
router := mux.NewRouter()
router.HandleFunc("/", ServeUI).Methods("GET")
apiRouter := router.PathPrefix("/api").Subrouter()
authRouter := apiRouter.PathPrefix("/auth").Subrouter()
authRouter.HandleFunc("/login", HandleLogin).Methods("POST")
authRouter.HandleFunc("/logout", HandleLogout).Methods("POST")
entitiesRouter := apiRouter.PathPrefix("/entities").Subrouter()
entitiesRouter.HandleFunc("/", GetEntities).Methods("GET")
commonAPIMiddleware := negroni.New(corsMiddleware.NewCorsMiddleware())
router.PathPrefix("/api/auth").Handler(commonAPIMiddleware.With(
negroni.Wrap(authRouter),
))
router.PathPrefix("/api/entities").Handler(commonAPIMiddleware.With(
auth.NewAPIAuthMiddleware(),
negroni.Wrap(entitiesRouter),
))
n := negroni.New(negronilogrus.NewMiddleware())
n.UseHandler(router)
n.Run(":8009")
The code for corsMiddleware is as follows:
// CorsMiddleware allows CORS request for api routes
type CorsMiddleware struct {
}
// Negroni compatible interface
func (m *CorsMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
log.Info("CORS MIDDLEWARE CALLED")
if r.Method == http.MethodOptions && r.Header.Get("Access-Control-Request-Method") != "" {
log.Info("ServeHTTP: Preflight request")
handlePreflight(w, r)
// Preflight requests are standalone and should stop the chain as some other
// middleware may not handle OPTIONS requests correctly. One typical example
// is authentication middleware ; OPTIONS requests won't carry authentication
// headers (see #1)
w.WriteHeader(http.StatusOK)
} else {
log.Info("ServeHTTP: Actual request")
handleActualRequest(w, r)
next(w, r)
}
}
// handlePreflight handles pre-flight CORS requests
func handlePreflight(w http.ResponseWriter, r *http.Request) {
headers := w.Header()
origin := r.Header.Get("Origin")
if r.Method != http.MethodOptions {
log.Info(" Preflight aborted: %s!=OPTIONS", r.Method)
return
}
// Always set Vary headers
// see https://github.com/rs/cors/issues/10,
// https://github.com/rs/cors/commit/dbdca4d95feaa7511a46e6f1efb3b3aa505bc43f#commitcomment-12352001
headers.Add("Vary", "Origin")
headers.Add("Vary", "Access-Control-Request-Method")
headers.Add("Vary", "Access-Control-Request-Headers")
if origin == "" {
log.Info(" Preflight aborted: empty origin")
return
}
headers.Set("Access-Control-Allow-Origin", origin)
// Spec says: Since the list of methods can be unbounded, simply returning the method indicated
// by Access-Control-Request-Method (if supported) can be enough
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
headers.Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, X-Requested-With")
headers.Set("Access-Control-Allow-Credentials", "true")
headers.Set("Access-Control-Max-Age", strconv.Itoa(1000))
log.Info(" Preflight response headers: %v", headers)
}
// handleActualRequest handles simple cross-origin requests, actual request or redirects
func handleActualRequest(w http.ResponseWriter, r *http.Request) {
log.Info("CORS HANDLING ACTUAL REQUEST")
headers := w.Header()
origin := r.Header.Get("Origin")
if r.Method == http.MethodOptions {
log.Info(" Actual request no headers added: method == %s", r.Method)
return
}
// Always set Vary, see https://github.com/rs/cors/issues/10
headers.Add("Vary", "Origin")
if origin == "" {
log.Info(" Actual request no headers added: missing origin")
return
}
headers.Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, X-Requested-With")
headers.Set("Access-Control-Allow-Credentials", "true")
headers.Set("Access-Control-Allow-Origin", origin)
if true {
headers.Set("Access-Control-Allow-Credentials", "true")
}
}
Upvotes: 1
Views: 1037