package server import ( "crypto/rand" "fmt" "io/ioutil" "log" "net/http" "time" "github.com/dgrijalva/jwt-go" "github.com/khlieng/dispatch/storage" ) const ( cookieName = "dispatch" ) var ( hmacKey []byte ) func initAuth() { var err error hmacKey, err = getHMACKey() if err != nil { log.Fatal(err) } } func getHMACKey() ([]byte, error) { key, err := ioutil.ReadFile(storage.Path.HMACKey()) if err != nil { key = make([]byte, 32) rand.Read(key) err = ioutil.WriteFile(storage.Path.HMACKey(), key, 0600) if err != nil { return nil, err } } return key, nil } func handleAuth(w http.ResponseWriter, r *http.Request) *Session { var session *Session cookie, err := r.Cookie(cookieName) if err != nil { authLog(r, "No cookie set") session = newUser(w, r) } else { token, err := parseToken(cookie.Value) if err == nil && token.Valid { claims := token.Claims.(jwt.MapClaims) userID := uint64(claims["UserID"].(float64)) log.Println(r.RemoteAddr, "[Auth] GET", r.URL.Path, "| Valid token | User ID:", userID) session = sessions.get(userID) if session == nil { // A previous anonymous session has been cleaned up, create a new one session = newUser(w, r) } } else { if err != nil { authLog(r, "Invalid token: "+err.Error()) } else { authLog(r, "Invalid token") } session = newUser(w, r) } } return session } func newUser(w http.ResponseWriter, r *http.Request) *Session { user, err := storage.NewUser() if err != nil { return nil } log.Println(r.RemoteAddr, "[Auth] Create session | User ID:", user.ID) session := NewSession(user) sessions.set(user.ID, session) go session.run() token := jwt.New(jwt.SigningMethodHS256) claims := token.Claims.(jwt.MapClaims) claims["UserID"] = user.ID tokenString, err := token.SignedString(hmacKey) if err != nil { return nil } http.SetCookie(w, &http.Cookie{ Name: cookieName, Value: tokenString, Path: "/", Expires: time.Now().AddDate(0, 1, 0), HttpOnly: true, Secure: r.TLS != nil, }) return session } func parseToken(cookie string) (*jwt.Token, error) { return jwt.Parse(cookie, func(token *jwt.Token) (interface{}, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) } return hmacKey, nil }) } func authLog(r *http.Request, s string) { log.Println(r.RemoteAddr, "[Auth] GET", r.URL.Path, "|", s) }