package auth import ( "crypto/hmac" "crypto/sha256" "encoding/base64" "fmt" "net/http" "strconv" "strings" "time" ) const ( cookieName = "shop_session" sessionTTL = 24 * time.Hour ) type Session struct { secret []byte } func NewSession(secret string) *Session { return &Session{secret: []byte(secret)} } func (s *Session) Create(email string) (string, error) { exp := time.Now().Add(sessionTTL).Unix() payload := fmt.Sprintf("%s|%d", email, exp) sig := s.sign(payload) token := base64.URLEncoding.EncodeToString([]byte(payload + "|" + sig)) return token, nil } func (s *Session) Validate(token string) (string, bool) { raw, err := base64.URLEncoding.DecodeString(token) if err != nil { return "", false } parts := strings.Split(string(raw), "|") if len(parts) != 3 { return "", false } email, expStr, sig := parts[0], parts[1], parts[2] payload := email + "|" + expStr if !hmac.Equal([]byte(sig), []byte(s.sign(payload))) { return "", false } exp, err := strconv.ParseInt(expStr, 10, 64) if err != nil || time.Now().Unix() > exp { return "", false } return email, true } func (s *Session) SetCookie(w http.ResponseWriter, token string) { http.SetCookie(w, &http.Cookie{ Name: cookieName, Value: token, Path: "/admin", HttpOnly: true, SameSite: http.SameSiteLaxMode, MaxAge: int(sessionTTL.Seconds()), }) } func (s *Session) ClearCookie(w http.ResponseWriter) { http.SetCookie(w, &http.Cookie{ Name: cookieName, Value: "", Path: "/admin", HttpOnly: true, MaxAge: -1, }) } func (s *Session) FromRequest(r *http.Request) (string, bool) { c, err := r.Cookie(cookieName) if err != nil { return "", false } return s.Validate(c.Value) } func (s *Session) sign(payload string) string { mac := hmac.New(sha256.New, s.secret) mac.Write([]byte(payload)) return base64.URLEncoding.EncodeToString(mac.Sum(nil)) }