package auth import ( "context" "crypto/hmac" "crypto/rand" "crypto/sha256" "encoding/base64" "encoding/hex" "net/http" "strconv" "strings" "time" ) const cookieName = "cpone_session" const projectCookieName = "cpone_project_mcu_id" type ctxKey string const userCtxKey ctxKey = "username" // Username returns the logged-in username from the request context. func Username(r *http.Request) string { v, _ := r.Context().Value(userCtxKey).(string) return v } func setContext(r *http.Request, username string) *http.Request { return r.WithContext(context.WithValue(r.Context(), userCtxKey, username)) } func SetSession(w http.ResponseWriter, username, secret string) { val := encode(username) + "." + sign(username, secret) http.SetCookie(w, &http.Cookie{ Name: cookieName, Value: val, Path: "/", HttpOnly: true, SameSite: http.SameSiteLaxMode, Expires: time.Now().Add(24 * time.Hour), }) } func ClearSession(w http.ResponseWriter) { http.SetCookie(w, &http.Cookie{ Name: cookieName, Value: "", Path: "/", MaxAge: -1, }) } func SetSelectedProject(w http.ResponseWriter, mcuID int) { http.SetCookie(w, &http.Cookie{ Name: projectCookieName, Value: strconv.Itoa(mcuID), Path: "/", HttpOnly: true, SameSite: http.SameSiteLaxMode, Expires: time.Now().Add(7 * 24 * time.Hour), }) } func ClearSelectedProject(w http.ResponseWriter) { http.SetCookie(w, &http.Cookie{ Name: projectCookieName, Value: "", Path: "/", MaxAge: -1, }) } func SelectedProjectID(r *http.Request) int { c, err := r.Cookie(projectCookieName) if err != nil { return 0 } id, err := strconv.Atoi(strings.TrimSpace(c.Value)) if err != nil { return 0 } return id } func getSession(r *http.Request, secret string) string { c, err := r.Cookie(cookieName) if err != nil { return "" } parts := strings.SplitN(c.Value, ".", 2) if len(parts) != 2 { return "" } username, err := decode(parts[0]) if err != nil || username == "" { return "" } if !hmac.Equal([]byte(sign(username, secret)), []byte(parts[1])) { return "" } return username } func newUUID() string { b := make([]byte, 16) rand.Read(b) return hex.EncodeToString(b) } func sign(username, secret string) string { mac := hmac.New(sha256.New, []byte(secret)) mac.Write([]byte(username)) return base64.RawURLEncoding.EncodeToString(mac.Sum(nil)) } func encode(s string) string { return base64.RawURLEncoding.EncodeToString([]byte(s)) } func decode(s string) (string, error) { b, err := base64.RawURLEncoding.DecodeString(s) return string(b), err }