This commit is contained in:
ponzischeme89
2026-08-10 08:37:08 +12:00
parent d2f2eb62be
commit 78d26effbf
18 changed files with 592 additions and 44 deletions
+27 -15
View File
@@ -17,13 +17,13 @@ import (
const (
installerCookieName = "memby_installer"
installerSessionTTL = 30 * time.Minute
adminSessionTTL = 12 * time.Hour
installerDeviceID = "memby-web-installer"
installerDeviceName = "Memby Web Installer"
// installerRenewWithin is how close to expiry a session must be before an operator's
// own request re-issues it. Half the TTL, so a cookie is rewritten at most once every
// fifteen minutes rather than on every request of a working session.
installerRenewWithin = installerSessionTTL / 2
// adminRenewWithin is how close to expiry a session must be before an operator's own
// request re-issues it. Half the TTL avoids rewriting the cookie on every request.
adminRenewWithin = adminSessionTTL / 2
)
func (s *Server) installerSecret() []byte {
@@ -45,8 +45,12 @@ func (s *Server) signInstallerValue(purpose string, payload []byte) []byte {
}
func (s *Server) newInstallerSession() (string, error) {
return s.newBrowserSession(installerSessionTTL)
}
func (s *Server) newBrowserSession(ttl time.Duration) (string, error) {
payload := make([]byte, 8+16)
binary.BigEndian.PutUint64(payload[:8], uint64(time.Now().Add(installerSessionTTL).Unix()))
binary.BigEndian.PutUint64(payload[:8], uint64(time.Now().Add(ttl).Unix()))
if _, err := rand.Read(payload[8:]); err != nil {
return "", err
}
@@ -79,7 +83,7 @@ func (s *Server) installerSessionExpiry(r *http.Request) (time.Time, bool) {
}
expires := int64(binary.BigEndian.Uint64(payload[:8]))
now := time.Now().Unix()
if expires <= now || expires > now+int64(installerSessionTTL/time.Second)+60 {
if expires <= now || expires > now+int64(adminSessionTTL/time.Second)+60 {
return time.Time{}, false
}
return time.Unix(expires, 0), true
@@ -90,31 +94,35 @@ func (s *Server) validInstallerSession(r *http.Request) bool {
return ok
}
// renewInstallerSession slides a valid session's expiry forward. The TTL was absolute and
// renewAdminSession slides a valid session's expiry forward. The TTL was absolute and
// nothing extended it, so an operator working the admin console was signed out from under
// themselves after thirty minutes and the page's poll became a permanent "invalid admin
// themselves and the page's poll became a permanent "invalid admin
// token" banner with no sign-in to return to. Callers must only reach here for a request
// an operator actually made — see operatorPresent — or an abandoned tab's own polling
// would keep the session alive indefinitely, which is what the TTL exists to stop.
func (s *Server) renewInstallerSession(w http.ResponseWriter, r *http.Request) {
func (s *Server) renewAdminSession(w http.ResponseWriter, r *http.Request) {
expires, ok := s.installerSessionExpiry(r)
if !ok || time.Until(expires) > installerRenewWithin {
if !ok || time.Until(expires) > adminRenewWithin {
return
}
session, err := s.newInstallerSession()
session, err := s.newBrowserSession(adminSessionTTL)
if err != nil {
s.loggerFor(r.Context()).Error("installer session renewal failed", "error", err)
return
}
s.setInstallerCookie(w, session)
s.setBrowserSessionCookie(w, session, adminSessionTTL)
}
func (s *Server) setInstallerCookie(w http.ResponseWriter, value string) {
s.setBrowserSessionCookie(w, value, installerSessionTTL)
}
func (s *Server) setBrowserSessionCookie(w http.ResponseWriter, value string, ttl time.Duration) {
http.SetCookie(w, &http.Cookie{
Name: installerCookieName,
Value: value,
Path: "/",
MaxAge: int(installerSessionTTL / time.Second),
MaxAge: int(ttl / time.Second),
HttpOnly: true,
Secure: true,
SameSite: http.SameSiteStrictMode,
@@ -210,13 +218,17 @@ func (s *Server) handleInstallLogin(w http.ResponseWriter, r *http.Request) {
return
}
session, err := s.newInstallerSession()
ttl := installerSessionTTL
if strings.HasPrefix(next, "/admin/") {
ttl = adminSessionTTL
}
session, err := s.newBrowserSession(ttl)
if err != nil {
s.loggerFor(r.Context()).Error("installer session generation failed", "error", err)
writeError(w, http.StatusInternalServerError, "could not start installer session")
return
}
s.setInstallerCookie(w, session)
s.setBrowserSessionCookie(w, session, ttl)
http.Redirect(w, r, next, http.StatusSeeOther)
}