Fix CORS preflight checks requiring authentication

This commit is contained in:
Patrick Pacher 2022-07-27 14:57:05 +02:00 committed by Daniel
parent e5b8dd77da
commit 2431914756

View file

@ -106,10 +106,9 @@ func (mh *mainHandler) handle(w http.ResponseWriter, r *http.Request) error {
}()
// Check Cross-Origin Requests.
isCrossSite := false
origin := r.Header.Get("Origin")
isPreflighCheck := false
if origin != "" {
isCrossSite = true
// Parse origin URL.
originURL, err := url.Parse(origin)
@ -125,6 +124,9 @@ func (mh *mainHandler) handle(w http.ResponseWriter, r *http.Request) error {
// Origin (with port) matches Host.
case originURL.Hostname() == r.Host:
// Origin (without port) matches Host.
case originURL.Scheme == "chrome-extension":
// Allow access for the browser extension
// TODO(ppacher): can we improve that check here?
case devMode() &&
utils.StringInSlice(allowedDevCORSOrigins, originURL.Hostname()):
// We are in dev mode and the request is coming from the allowed
@ -138,6 +140,22 @@ func (mh *mainHandler) handle(w http.ResponseWriter, r *http.Request) error {
// If the Host header has a port, and the Origin does not, requests will
// also end up here, as we cannot properly check for equality.
}
// Add Cross-Site Headers now as we need them in any case now.
w.Header().Set("Access-Control-Allow-Origin", origin)
w.Header().Set("Access-Control-Allow-Methods", "*")
w.Header().Set("Access-Control-Allow-Headers", "*")
w.Header().Set("Access-Control-Allow-Credentials", "true")
w.Header().Set("Access-Control-Expose-Headers", "*")
w.Header().Set("Access-Control-Max-Age", "60")
w.Header().Add("Vary", "Origin")
// if there's a Access-Control-Request-Method header this is a Preflight check.
// In that case, we will just check if the preflighMethod is allowed and then return
// success here
if preflighMethod := r.Header.Get("Access-Control-Request-Method"); r.Method == http.MethodOptions && preflighMethod != "" {
isPreflighCheck = true
}
}
// Clean URL.
@ -184,21 +202,6 @@ func (mh *mainHandler) handle(w http.ResponseWriter, r *http.Request) error {
return nil
}
// Check authentication.
apiRequest.AuthToken = authenticateRequest(lrw, r, handler, readMethod)
if apiRequest.AuthToken == nil {
// Authenticator already replied.
return nil
}
// Wait for the owning module to be ready.
if moduleHandler, ok := handler.(ModuleHandler); ok {
if !moduleIsReady(moduleHandler.BelongsTo()) {
http.Error(lrw, "The API endpoint is not ready yet. Please try again later.", http.StatusServiceUnavailable)
return nil
}
}
// Add security headers.
w.Header().Set("Referrer-Policy", "no-referrer")
w.Header().Set("X-Content-Type-Options", "nosniff")
@ -217,15 +220,28 @@ func (mh *mainHandler) handle(w http.ResponseWriter, r *http.Request) error {
)
}
// Add Cross-Site Headers when handling Cross-Site Requests.
if isCrossSite {
w.Header().Set("Access-Control-Allow-Origin", origin)
w.Header().Set("Access-Control-Allow-Methods", "*")
w.Header().Set("Access-Control-Allow-Headers", "*")
w.Header().Set("Access-Control-Allow-Credentials", "true")
w.Header().Set("Access-Control-Expose-Headers", "*")
w.Header().Set("Access-Control-Max-Age", "60")
w.Header().Add("Vary", "Origin")
// At this point we know the method is allowed and there's a handler for the request.
// If this is just a CORS-Preflight, we'll accept the request with StatusOK now.
// There's no point in trying to authenticate the request because the Browser will
// not send authentication along a preflight check.
if isPreflighCheck && handler != nil {
lrw.WriteHeader(http.StatusOK)
return nil
}
// Check authentication.
apiRequest.AuthToken = authenticateRequest(lrw, r, handler, readMethod)
if apiRequest.AuthToken == nil {
// Authenticator already replied.
return nil
}
// Wait for the owning module to be ready.
if moduleHandler, ok := handler.(ModuleHandler); ok {
if !moduleIsReady(moduleHandler.BelongsTo()) {
http.Error(lrw, "The API endpoint is not ready yet. Please try again later.", http.StatusServiceUnavailable)
return nil
}
}
// Check if we have a handler.