summaryrefslogtreecommitdiff
path: root/internal/handlers/auth.go
diff options
context:
space:
mode:
authorGrail Finder <wohilas@gmail.com>2025-03-29 11:12:53 +0300
committerGrail Finder <wohilas@gmail.com>2025-03-29 11:12:53 +0300
commit3921db6166e2da895257496bb76dd115556699d3 (patch)
tree1be4f739121761085f69cb7706c60dbbe98a93e9 /internal/handlers/auth.go
init
Diffstat (limited to 'internal/handlers/auth.go')
-rw-r--r--internal/handlers/auth.go162
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
+}