Go · 2425 bytes Raw Blame History
1 // SPDX-License-Identifier: AGPL-3.0-or-later
2
3 package auth
4
5 import (
6 "net/http"
7 "time"
8
9 "github.com/tenseleyFlow/shithub/internal/web/middleware"
10 )
11
12 // settingsSessionsList renders GET /settings/sessions.
13 //
14 // V1 surfaces only the current session (we use AEAD-encrypted cookies,
15 // no server-side session table — there's no enumerable list). The page's
16 // real value is the "Sign out everywhere" affordance, which bumps
17 // users.session_epoch and invalidates every cookie that carries the old
18 // epoch on its next request.
19 func (h *Handlers) settingsSessionsList(w http.ResponseWriter, r *http.Request) {
20 h.renderSessionsList(w, r, "")
21 }
22
23 // settingsSessionsLogoutAll handles POST /settings/sessions/logout-everywhere.
24 func (h *Handlers) settingsSessionsLogoutAll(w http.ResponseWriter, r *http.Request) {
25 user := middleware.CurrentUserFromContext(r.Context())
26
27 if err := h.q.BumpUserSessionEpoch(r.Context(), h.d.Pool, user.ID); err != nil {
28 h.d.Logger.ErrorContext(r.Context(), "sessions: bump", "error", err)
29 h.d.Render.HTTPError(w, r, http.StatusInternalServerError, "")
30 return
31 }
32
33 // Re-sync the current session's epoch so this browser doesn't sign
34 // itself out alongside the others.
35 epoch, err := h.q.GetUserSessionEpoch(r.Context(), h.d.Pool, user.ID)
36 if err != nil {
37 h.d.Logger.ErrorContext(r.Context(), "sessions: read epoch", "error", err)
38 h.d.Render.HTTPError(w, r, http.StatusInternalServerError, "")
39 return
40 }
41 s := middleware.SessionFromContext(r.Context())
42 s.Epoch = epoch
43 if err := h.d.SessionStore.Save(w, r, s); err != nil {
44 h.d.Logger.ErrorContext(r.Context(), "sessions: save", "error", err)
45 h.d.Render.HTTPError(w, r, http.StatusInternalServerError, "")
46 return
47 }
48
49 h.renderSessionsList(w, r, "Signed out of every other session.")
50 }
51
52 // renderSessionsList is the shared render path. The "current session"
53 // row pulls IssuedAt out of the loaded session struct.
54 func (h *Handlers) renderSessionsList(w http.ResponseWriter, r *http.Request, successMsg string) {
55 s := middleware.SessionFromContext(r.Context())
56 issued := time.Unix(s.IssuedAt, 0)
57
58 h.renderPage(w, r, "settings/sessions", map[string]any{
59 "Title": "Sessions",
60 "CSRFToken": middleware.CSRFTokenForRequest(r),
61 "SettingsActive": "sessions",
62 "Issued": issued,
63 "UserAgent": r.Header.Get("User-Agent"),
64 "ClientIP": clientIP(r),
65 "Success": successMsg,
66 })
67 }
68