Handshake authorisation using Sessions - trevex/golem GitHub Wiki
Sometimes you want limited access to a router. The most common reason is to limit communication to authorised users only. In most web framework sessions are used to identify users. Since the handshake is basically a http request accessing the session cookie during this period is possible as well. This article will step you through the session example from the example repository.
The example is simple it provide a /login path, that authorises the user, and a /logout path to unauthorise. Both paths redirect to the html page of the example. In a production environment /login would validate a POST-request providing user login credentials.
Since golem doesn't provide a session system gorilla's sessions are used. We will need our secret and a session name used for cookie association:
const (
secret = "super-secret-key"
sessionName = "golem.sid"
)
We are using the most simple way to create a session store (gorilla's implementation has actually more to offer for more information look at their documentation):
var store = sessions.NewCookieStore([]byte(secret))
The /login and /logout paths will be handled by two simple handlers we create:
http.HandleFunc("/login", loginHandler)
http.HandleFunc("/logout", logoutHandler)
The login handler checks the session, we ignore the second argument when getting the session, because we don't care if it previously did not exist, we'll create it anyway if not. We set a the isAuthorized value to true and redirect to our example page:
func loginHandler(w http.ResponseWriter, r *http.Request) {
session, _ := store.Get(r, sessionName)
session.Values["isAuthorized"] = true
session.Save(r, w)
http.Redirect(w, r, "/example_session.html", http.StatusFound)
}
Unfortunately the in-memory session store of gorilla doesn't support removing of sessions, so we simply check if the session exists and if it does we delete the isAuthorized key to unauthorise the session:
func logoutHandler(w http.ResponseWriter, r *http.Request) {
if session, err := store.Get(r, sessionName); err == nil {
delete(session.Values, "isAuthorized")
session.Save(r, w)
}
http.Redirect(w, r, "/example_session.html", http.StatusFound)
}
Now the user is able to login and logout, the only step left is to create a router that checks the authentication of the user and only allows connections that were successfully verified. To do such things golem provides the ability to set a handshake verification callback. The function will be called prior to upgrading the http request and the boolean it returns defines if a connection is accepted or not. The handshake callback needs to be of type func (http.ResponseWriter, *http.Request) bool and in our example would set it like this:
myrouter := golem.NewRouter()
myrouter.OnHandshake(validateSession)
The validateSession function will now authorise our users by checking if the session is valid and if the isAuthorized key is set:
func validateSession(w http.ResponseWriter, r *http.Request) bool {
if session, err := store.Get(r, sessionName); err == nil {
if v, ok := session.Values["isAuthorized"]; ok && v == true {
fmt.Println("Authorized user identified!")
return true
} else {
fmt.Println("Unauthorized user detected!")
return false
}
}
}
The only step left on the server-side is exposing the hander function of the router and to start listening:
http.HandleFunc("/ws", myrouter.Handler())
if err := http.ListenAndServe(*addr, nil); err != nil {
log.Fatal("ListenAndServe:", err)
}
The client-side is really simple two buttons are provided to access the /login and logout paths and the user is always redirect to the example page and should see a connection error in the console if the user is unauthorized. The client and server source files can be found in the example repository.