src/server/internal/api/anonymous_owner.go
1,804 bytes · 74 lines · capsule://quake0day/[email protected]
raw on github
package api
import (
"crypto/rand"
"encoding/base64"
"errors"
"net/http"
"strings"
"time"
)
const (
anonymousOwnerCookieName = "cyberverse_anonymous_owner"
anonymousOwnerTTL = 180 * 24 * time.Hour
anonymousOwnerPrefix = "anon:"
)
func (r *Router) ensureAnonymousOwner(w http.ResponseWriter, req *http.Request) (string, error) {
if ownerID, ok := anonymousOwnerFromRequest(req); ok {
return ownerID, nil
}
token, err := randomOwnerToken()
if err != nil {
return "", err
}
http.SetCookie(w, anonymousOwnerCookie(req, token))
return anonymousOwnerPrefix + token, nil
}
func anonymousOwnerFromRequest(req *http.Request) (string, bool) {
cookie, err := req.Cookie(anonymousOwnerCookieName)
if err != nil || cookie.Value == "" {
return "", false
}
token := strings.TrimSpace(cookie.Value)
if !validOwnerToken(token) {
return "", false
}
return anonymousOwnerPrefix + token, true
}
func randomOwnerToken() (string, error) {
buf := make([]byte, 32)
if _, err := rand.Read(buf); err != nil {
return "", errors.New("failed to generate anonymous owner")
}
return base64.RawURLEncoding.EncodeToString(buf), nil
}
func validOwnerToken(token string) bool {
raw, err := base64.RawURLEncoding.DecodeString(token)
return err == nil && len(raw) == 32
}
func anonymousOwnerCookie(req *http.Request, token string) *http.Cookie {
return &http.Cookie{
Name: anonymousOwnerCookieName,
Value: token,
Path: "/",
Expires: time.Now().Add(anonymousOwnerTTL),
MaxAge: int(anonymousOwnerTTL.Seconds()),
HttpOnly: true,
SameSite: http.SameSiteLaxMode,
Secure: requestIsHTTPS(req),
}
}
func requestIsHTTPS(req *http.Request) bool {
if req.TLS != nil {
return true
}
return strings.EqualFold(req.Header.Get("X-Forwarded-Proto"), "https")
}