1

I'm currently writing a HTTP server(net/http) which hosts multiple endpoints and requires client authentication(step 1) before accessing these endpoints. Upon successful authentication, the server issues a short-lived token which the client then use to access these endpoints. when the client sends the token(via HTTP Header), have a piece of code at the start of every handler function to check the client is authenticated and presented token is valid. I'm looking for a hook/wrapper which can intercept and validate the client instead of calling isAuthenticated(r) from every endpoint function.

func getMyEndpoint(w http.ResponseWriter, r *http.Request) {
        if valid := isAuthenticated(r); !valid {
            w.WriteHeader(http.StatusUnauthorized)
            io.WriteString(w, "Invalid token or Client not authenticated."
            return
        }
        ...
}

func server() {

        http.HandleFunc("/login", clientLoginWithCertAuth)
        http.HandleFunc("/endpoint1", getMyEndpoint)
        http.HandleFunc("/endpoint2", putMyEndpoint)

        server := &http.Server{
                Addr: ":443",
                TLSConfig: &tls.Config{
                        ClientCAs:  caCertPool,
                        ClientAuth: tls.VerifyClientCertIfGiven,
                },
        }

        if err := server.ListenAndServeTLS("cert.pem", "key.pem"); err != nil {
            panic(err)
        }
}
Pr1614
  • 61
  • 1
  • 6
  • "I'm looking for a hook/wrapper which can intercept and validate the client instead of calling isAuthenticated(r) from every endpoint function." What does that mean? How would the hook validate the client if not by calling isAuthenticated? Or are you asking how to write a http.HandlerFunc which acts as such a wrapper? – Volker Mar 31 '20 at 07:34
  • I guess that's what the OP is asking for. – Tom Mar 31 '20 at 07:34

1 Answers1

3

You can create a function that can wrap a http.HandlerFunc, e.g. like this:

func handleAuth(f http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        if valid := isAuthenticated(r); !valid {
            w.WriteHeader(http.StatusUnauthorized)
            io.WriteString(w, "Invalid token or Client not authenticated.")
            return // this return is *very* important
        }
        // Now call the actual handler, which is authenticated
        f(w, r)
    }
}

Now you also need to register your handlers to use it by wrapping it around your other http.HandlerFuncs (only those that need authentication obviously):

func server() {
        // No authentication for /login
        http.HandleFunc("/login", clientLoginWithCertAuth)

        // Authentication required
        http.HandleFunc("/endpoint1", handleAuth(getMyEndpoint))
        http.HandleFunc("/endpoint2", handleAuth(putMyEndpoint))

        server := &http.Server{
                Addr: ":443",
                TLSConfig: &tls.Config{
                        ClientCAs:  caCertPool,
                        ClientAuth: tls.VerifyClientCertIfGiven,
                },
        }

        if err := server.ListenAndServeTLS("cert.pem", "key.pem"); err != nil {
            panic(err)
        }
}

That way, your handlers only get called (by handleAuth) if isAuthenticated returns true for that request, without duplicating the code in all of them.

xarantolus
  • 1,961
  • 1
  • 11
  • 19