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)
}
}
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.HandlerFunc
s (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.