diff options
Diffstat (limited to 'internal/handlers/auth.go')
-rw-r--r-- | internal/handlers/auth.go | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/internal/handlers/auth.go b/internal/handlers/auth.go new file mode 100644 index 0000000..d0ccdff --- /dev/null +++ b/internal/handlers/auth.go @@ -0,0 +1,162 @@ +package handlers + +import ( + "crypto/hmac" + "crypto/sha256" + "demoon/internal/models" + "demoon/pkg/utils" + "encoding/base64" + "encoding/json" + "html/template" + "net/http" + "strings" + "time" + + "golang.org/x/crypto/bcrypt" +) + +func abortWithError(w http.ResponseWriter, msg string) { + tmpl := template.Must(template.ParseGlob("components/*.html")) + tmpl.ExecuteTemplate(w, "error", msg) +} + +func (h *Handlers) HandleSignup(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + username := r.PostFormValue("username") + if username == "" { + msg := "username not provided" + h.log.Error(msg) + abortWithError(w, msg) + return + } + password := r.PostFormValue("password") + if password == "" { + msg := "password not provided" + h.log.Error(msg) + abortWithError(w, msg) + return + } + // make sure username does not exists + cleanName := utils.RemoveSpacesFromStr(username) + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), 8) + // create user in db + now := time.Now() + nextMidnight := time.Date(now.Year(), now.Month(), now.Day(), + 0, 0, 0, 0, time.UTC).Add(time.Hour * 24) + newUser := &models.UserScore{ + Username: cleanName, Password: string(hashedPassword), + BurnTime: nextMidnight, CreatedAt: now, + } + // login user + cookie, err := h.makeCookie(cleanName, r.RemoteAddr) + if err != nil { + h.log.Error("failed to login", "error", err) + abortWithError(w, err.Error()) + return + } + http.SetCookie(w, cookie) + // http.Redirect(w, r, "/", 302) + tmpl, err := template.ParseGlob("components/*.html") + if err != nil { + abortWithError(w, err.Error()) + return + } + tmpl.ExecuteTemplate(w, "main", newUser) +} + +func (h *Handlers) HandleLogin(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + username := r.PostFormValue("username") + if username == "" { + msg := "username not provided" + h.log.Error(msg) + abortWithError(w, msg) + return + } + password := r.PostFormValue("password") + if password == "" { + msg := "password not provided" + h.log.Error(msg) + abortWithError(w, msg) + return + } + cleanName := utils.RemoveSpacesFromStr(username) + tmpl, err := template.ParseGlob("components/*.html") + if err != nil { + abortWithError(w, err.Error()) + return + } + cookie, err := h.makeCookie(cleanName, r.RemoteAddr) + if err != nil { + h.log.Error("failed to login", "error", err) + abortWithError(w, err.Error()) + return + } + http.SetCookie(w, cookie) + tmpl.ExecuteTemplate(w, "main", nil) +} + +func (h *Handlers) makeCookie(username string, remote string) (*http.Cookie, error) { + // secret + // Create a new random session token + // sessionToken := xid.New().String() + sessionToken := "token" + expiresAt := time.Now().Add(time.Duration(h.cfg.SessionLifetime) * time.Second) + // Set the token in the session map, along with the session information + session := &models.Session{ + Username: username, + Expiry: expiresAt, + } + cookieName := "session_token" + // hmac to protect cookies + hm := hmac.New(sha256.New, []byte(h.cfg.CookieSecret)) + hm.Write([]byte(cookieName)) + hm.Write([]byte(sessionToken)) + signature := hm.Sum(nil) + // b64 enc to avoid non-ascii + cookieValue := base64.URLEncoding.EncodeToString([]byte( + string(signature) + sessionToken)) + cookie := &http.Cookie{ + Name: cookieName, + Value: cookieValue, + Secure: true, + HttpOnly: true, + SameSite: http.SameSiteNoneMode, + Domain: h.cfg.ServerConfig.Host, + } + h.log.Info("check remote addr for cookie set", + "remote", remote, "session", session) + if strings.Contains(remote, "192.168.0") { + // no idea what is going on + cookie.Domain = "192.168.0.101" + } + // set ctx? + // set user in session + if err := h.cacheSetSession(sessionToken, session); err != nil { + return nil, err + } + return cookie, nil +} + +func (h *Handlers) cacheGetSession(key string) (*models.Session, error) { + userSessionB, err := h.mc.Get(key) + if err != nil { + return nil, err + } + var us *models.Session + if err := json.Unmarshal(userSessionB, &us); err != nil { + return nil, err + } + return us, nil +} + +func (h *Handlers) cacheSetSession(key string, session *models.Session) error { + sesb, err := json.Marshal(session) + if err != nil { + return err + } + h.mc.Set(key, sesb) + // expire in 10 min + h.mc.Expire(key, 10*60) + return nil +} |