From 2314b616717d2d749d71233595284f7f1cd2785a Mon Sep 17 00:00:00 2001 From: Branden J Brown Date: Fri, 2 Feb 2024 17:30:35 -0600 Subject: [PATCH] add logout button --- serve/session.go | 26 ++++++++++++++++++++++---- server.go | 24 ++++++++++++++++++++++++ site/src/App.vue | 4 +++- 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/serve/session.go b/serve/session.go index 92d1b23..04df2f9 100644 --- a/serve/session.go +++ b/serve/session.go @@ -34,9 +34,22 @@ func SetSession(w http.ResponseWriter, s player.Session) { }) } -// WithSession is a middleware that adds a player ID to the request context -// based on the session cookie content. If there is no such cookie, or its -// value is invalid, the request fails with a 401 error. +// RemoveSession clears the session token cookie on a response. +func RemoveSession(w http.ResponseWriter) { + http.SetCookie(w, &http.Cookie{ + Name: sessionCookie, + Value: "", + Path: "/", + Expires: time.Unix(0, 0), + Secure: true, + HttpOnly: true, + SameSite: http.SameSiteLaxMode, + }) +} + +// WithSession is a middleware that adds a player ID and session to the request +// context based on the session cookie content. If there is no such cookie, or +// its value is invalid, the request fails with a 401 error. func WithSession(sessions player.RowQuerier) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -55,7 +68,7 @@ func WithSession(sessions player.RowQuerier) func(http.Handler) http.Handler { http.Error(w, "unauthorized", http.StatusUnauthorized) return } - ctx := with(r.Context(), p) + ctx := with(with(r.Context(), id), p) next.ServeHTTP(w, r.WithContext(ctx)) }) } @@ -65,3 +78,8 @@ func WithSession(sessions player.RowQuerier) func(http.Handler) http.Handler { func ReqPlayer(ctx context.Context) player.ID { return value[player.ID](ctx) } + +// ReqSession returns the session ID set by WithSession in the request context. +func ReqSession(ctx context.Context) player.Session { + return value[player.Session](ctx) +} diff --git a/server.go b/server.go index dcbf82b..5490cec 100644 --- a/server.go +++ b/server.go @@ -147,6 +147,30 @@ func (s *Server) Login(w http.ResponseWriter, r *http.Request) { slog.InfoContext(ctx, "logged in", "player", p, "id", id) } +func (s *Server) Logout(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + slog := slog.With( + slog.String("remote", r.RemoteAddr), + slog.String("forwarded-for", r.Header.Get("X-Forwarded-For")), + slog.String("user-agent", r.UserAgent()), + ) + id := serve.ReqSession(ctx) + if id == (player.Session{}) { + slog.WarnContext(ctx, "no session on logout") + http.Error(w, "what", http.StatusUnauthorized) + panic("unreachable") + } + err := player.Logout(ctx, s.sessions, id) + if err != nil { + slog.ErrorContext(ctx, "logout failed", "err", err.Error()) + http.Error(w, "something went wrong", http.StatusInternalServerError) + return + } + serve.RemoveSession(w) + slog.InfoContext(ctx, "logged out", "session", id) + http.Redirect(w, r, "/", http.StatusSeeOther) +} + func (s *Server) Me(w http.ResponseWriter, r *http.Request) { id := serve.ReqPlayer(r.Context()) if id == (player.ID{}) { diff --git a/site/src/App.vue b/site/src/App.vue index fefd170..5ea7efc 100644 --- a/site/src/App.vue +++ b/site/src/App.vue @@ -13,7 +13,8 @@ - Play + Play + Log Out @@ -83,5 +84,6 @@ onMounted(async () => { } catch { me.value = null; } + me.value = 'a'; });