diff --git a/server/http.go b/server/http.go index a277bf945b6672e6d6dc79e38b4e6890ad1dce7c..52bdbe369d9bc254087fc6c846eeb95491fb5b64 100644 --- a/server/http.go +++ b/server/http.go @@ -295,30 +295,47 @@ func (h *Server) handleExchange(w http.ResponseWriter, req *http.Request) { // Handler returns the http.Handler for the SSO server application. func (h *Server) Handler() http.Handler { - m := mux.NewRouter() + // The root HTTP handler. This must be a gorilla/mux.Router since + // sessions depend on it. + root := mux.NewRouter() - var lih, loh http.Handler - lih = h.loginHandler - loh = h.withAuth(h.handleLogout) - if h.csrfSecret != nil { - csrfW := csrf.Protect(h.csrfSecret) - lih = csrfW(lih) - loh = csrfW(loh) - } - m.Handle("/login", withDynamicHeaders(lih)) - m.Handle("/logout", withDynamicHeaders(loh)) - - m.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(&assetfs.AssetFS{ + // Serve static content to anyone. + root.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(&assetfs.AssetFS{ Asset: Asset, AssetDir: AssetDir, AssetInfo: AssetInfo, Prefix: "static", }))) - m.Handle("/exchange", withDynamicHeaders(http.HandlerFunc(h.handleExchange))) - m.Handle("/", withDynamicHeaders(h.withAuth(h.handleHomepage))) + // Build the main IDP application router, with optional CSRF + // protection. + m := http.NewServeMux() + m.Handle("/login", h.loginHandler) + m.Handle("/logout", h.withAuth(h.handleLogout)) + idph := http.Handler(m) + if h.csrfSecret != nil { + idph = csrf.Protect(h.csrfSecret)(idph) + } + + // Add the SSO provider endpoints (root path and /exchange), + // which do not need CSRF. We use a HandlerFunc to bypass the + // '/' dispatch semantics of the standard http.ServeMux. + ssoh := h.withAuth(h.handleHomepage) + userh := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch { + case r.Method == "GET" && r.URL.Path == "/": + ssoh.ServeHTTP(w, r) + case r.URL.Path == "/exchange": + h.handleExchange(w, r) + default: + idph.ServeHTTP(w, r) + } + }) - return m + // User-facing routes require cache-busting and CSP headers. + root.PathPrefix("/").Handler(withDynamicHeaders(userh)) + + return root } // A relatively strict CSP.