Skip to content

Instantly share code, notes, and snippets.

@sdemingo
Last active February 24, 2016 18:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sdemingo/79d5b6651b728badbe46 to your computer and use it in GitHub Desktop.
Save sdemingo/79d5b6651b728badbe46 to your computer and use it in GitHub Desktop.
Basic users authentication over TLS in Go
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Login</title>
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
</head>
<body>
<form method="POST" action="/login">
<label>Login:</label><input type="text" name="username" />
<label>Password:</label><input type="text" name="password" />
<input type="submit" name="login" value="Go"/>
</form>
</body>
</html>
package main
import (
"log"
"net/http"
)
const (
TLSPORT = ":8443"
PORT = ":8080"
PRIV_KEY = "./private_key"
PUBLIC_KEY = "./public_key"
DOMAIN = "localhost"
)
func main() {
http.HandleFunc("/", rootHandler)
http.HandleFunc("/login", loginHandler)
http.HandleFunc("/logout", logoutHandler)
http.HandleFunc("/welcome", welcome)
fs := http.FileServer(http.Dir("static"))
http.Handle("/static/", http.StripPrefix("/static/", fs))
// Redirect all requests to TLS socket
go func() {
err := http.ListenAndServe(PORT, http.RedirectHandler("https://"+DOMAIN+TLSPORT, http.StatusFound))
if err != nil {
panic("Error: " + err.Error())
}
}()
// Listen on secure port
err := http.ListenAndServeTLS(TLSPORT, PUBLIC_KEY, PRIV_KEY, nil)
if err != nil {
panic("Error: " + err.Error())
}
}
func Exit(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/", http.StatusFound)
}
func rootHandler(w http.ResponseWriter, r *http.Request) {
s, _ := GetSession(r)
if s != nil {
welcome(w, r)
return
}
tmpl := template.Must(template.ParseFiles("login.html"))
if err := tmpl.Execute(w, nil); err != nil {
log.Printf("%v", err)
return
}
}
func loginHandler(w http.ResponseWriter, r *http.Request) {
s, _ := GetSession(r)
if s != nil {
welcome(w, r)
return
}
r.ParseForm()
username := r.Form.Get("username")
password := r.Form.Get("password")
// Check the password and recover the user from the database
user := &User{username, username, password}
log.Printf("User %s do login", user.Username)
session := NewSession(user)
sessionCookie := &http.Cookie{Name: "sessionKey", Value: session.Key, HttpOnly: false}
http.SetCookie(w, sessionCookie)
welcome(w, r)
}
func logoutHandler(w http.ResponseWriter, r *http.Request) {
s, _ := GetSession(r)
if s != nil {
log.Printf("User %s do login", s.User.Username)
}
DeleteSession(r)
Exit(w, r)
}
func welcome(w http.ResponseWriter, r *http.Request) {
_, err := GetSession(r)
if err != nil {
Exit(w, r)
return
}
tmpl := template.Must(template.ParseFiles("welcome.html"))
if err := tmpl.Execute(w, nil); err != nil {
log.Printf("%v", err)
return
}
}
package main
import (
"crypto/rand"
"fmt"
"log"
"net/http"
"time"
)
var sessionTable map[string]*Session
const (
TIME_MAX_INACTIVE_SESSION = 2 * time.Minute
TIME_SESSIONCLEANER_PERIOD = 1 * time.Minute
)
func init() {
sessionTable = make(map[string]*Session)
go sessionCleaner()
}
type Session struct {
Key string
User *User
LoginTime time.Time
LastTime time.Time
}
func sessionCleaner() {
log.Printf("Run session cleaner")
for {
for _, s := range sessionTable {
if time.Since(s.LastTime) > TIME_MAX_INACTIVE_SESSION {
delete(sessionTable, s.Key)
log.Printf("Session for user %s closed for inactivity", s.User.Username)
}
}
time.Sleep(TIME_SESSIONCLEANER_PERIOD)
}
}
func randKey() string {
b := make([]byte, 8)
rand.Read(b)
return fmt.Sprintf("%x", b)
}
func NewSession(u *User) *Session {
key := randKey()
s := &Session{key, u, time.Now(), time.Now()}
sessionTable[key] = s
return s
}
func GetSession(r *http.Request) (*Session, error) {
ck, err := r.Cookie("sessionKey")
if err != nil {
return nil, err
}
s, ok := sessionTable[ck.Value]
if !ok {
return nil, fmt.Errorf("No session for this key")
}
s.LastTime = time.Now()
return s, nil
}
func DeleteSession(r *http.Request) error {
s, err := GetSession(r)
if err != nil {
return err
}
delete(sessionTable, s.Key)
return nil
}
package main
import (
"fmt"
)
type User struct {
Fullname string
Username string
Password string
}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Login</title>
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
</head>
<body>
<h1>Welcome!!</h1>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment