From 5c8bec0ad265db5ccd1c354b3efb98aa7870289c Mon Sep 17 00:00:00 2001 From: Haelnorr Date: Fri, 14 Feb 2025 17:36:07 +1100 Subject: [PATCH] Added logout functionality and client error message for 500 status codes --- handlers/login.go | 1 - handlers/logout.go | 62 +++++++++++++++++++++++++++ server/routes.go | 3 +- static/css/input.css | 6 +++ view/component/errorPopup.templ | 63 ++++++++++++++++++++++++++++ view/component/footer/footer.templ | 4 +- view/component/form/loginform.templ | 20 +++++++-- view/component/nav/navbarright.templ | 1 + view/layout/global.templ | 16 ++++++- 9 files changed, 168 insertions(+), 8 deletions(-) create mode 100644 handlers/logout.go create mode 100644 view/component/errorPopup.templ diff --git a/handlers/login.go b/handlers/login.go index c4da38b..2b5f4a3 100644 --- a/handlers/login.go +++ b/handlers/login.go @@ -48,7 +48,6 @@ func HandleLoginRequest( config *config.Config, logger *zerolog.Logger, conn *sql.DB, - secretKey string, ) http.Handler { return http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { diff --git a/handlers/logout.go b/handlers/logout.go new file mode 100644 index 0000000..c8999a2 --- /dev/null +++ b/handlers/logout.go @@ -0,0 +1,62 @@ +package handlers + +import ( + "database/sql" + "net/http" + "projectreshoot/config" + "projectreshoot/cookies" + "projectreshoot/jwt" + + "github.com/pkg/errors" + "github.com/rs/zerolog" +) + +// Retrieve and revoke the user's tokens +func revokeTokens( + config *config.Config, + conn *sql.DB, + r *http.Request, +) error { + // get the tokens from the cookies + atStr, rtStr := cookies.GetTokenStrings(r) + aT, err := jwt.ParseAccessToken(config, conn, atStr) + if err != nil { + return errors.Wrap(err, "jwt.ParseAccessToken") + } + rT, err := jwt.ParseRefreshToken(config, conn, rtStr) + if err != nil { + return errors.Wrap(err, "jwt.ParseRefreshToken") + } + // revoke the refresh token first as the access token expires quicker + // only matters if there is an error revoking the tokens + err = jwt.RevokeToken(conn, rT) + if err != nil { + return errors.Wrap(err, "jwt.RevokeToken") + } + err = jwt.RevokeToken(conn, aT) + if err != nil { + return errors.Wrap(err, "jwt.RevokeToken") + } + return nil +} + +// Handle a logout request +func HandleLogout( + config *config.Config, + logger *zerolog.Logger, + conn *sql.DB, +) http.Handler { + return http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + err := revokeTokens(config, conn, r) + if err != nil { + logger.Error().Err(err).Msg("Error occured on user logout") + w.WriteHeader(http.StatusInternalServerError) + return + } + cookies.DeleteCookie(w, "access", "/") + cookies.DeleteCookie(w, "refresh", "/") + w.Header().Set("HX-Redirect", "/login") + }, + ) +} diff --git a/server/routes.go b/server/routes.go index 866b0e7..8576910 100644 --- a/server/routes.go +++ b/server/routes.go @@ -36,8 +36,9 @@ func addRoutes( config, logger, conn, - config.SecretKey, )) + // Logout + mux.Handle("POST /logout", handlers.HandleLogout(config, logger, conn)) // Profile page } diff --git a/static/css/input.css b/static/css/input.css index eeaeab5..c3b1527 100644 --- a/static/css/input.css +++ b/static/css/input.css @@ -1,12 +1,16 @@ @import url("https://fonts.googleapis.com/css2?family=Ubuntu+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap"); @import "tailwindcss"; +[x-cloak] { + display: none !important; +} @theme inline { --color-rosewater: var(--rosewater); --color-flamingo: var(--flamingo); --color-pink: var(--pink); --color-mauve: var(--mauve); --color-red: var(--red); + --color-dark-red: var(--dark-red); --color-maroon: var(--maroon); --color-peach: var(--peach); --color-yellow: var(--yellow); @@ -35,6 +39,7 @@ --pink: hsl(316, 73%, 69%); --mauve: hsl(266, 85%, 58%); --red: hsl(347, 87%, 44%); + --dark-red: hsl(343, 50%, 82%); --maroon: hsl(355, 76%, 59%); --peach: hsl(22, 99%, 52%); --yellow: hsl(35, 77%, 49%); @@ -64,6 +69,7 @@ --pink: hsl(316, 72%, 86%); --mauve: hsl(267, 84%, 81%); --red: hsl(343, 81%, 75%); + --dark-red: hsl(316, 19%, 27%); --maroon: hsl(350, 65%, 77%); --peach: hsl(23, 92%, 75%); --yellow: hsl(41, 86%, 83%); diff --git a/view/component/errorPopup.templ b/view/component/errorPopup.templ new file mode 100644 index 0000000..968beab --- /dev/null +++ b/view/component/errorPopup.templ @@ -0,0 +1,63 @@ +package component + +templ ErrorPopup() { +
+ +
+} diff --git a/view/component/footer/footer.templ b/view/component/footer/footer.templ index 4db4868..565166e 100644 --- a/view/component/footer/footer.templ +++ b/view/component/footer/footer.templ @@ -40,7 +40,9 @@ templ Footer() { > diff --git a/view/component/form/loginform.templ b/view/component/form/loginform.templ index a13bbd2..9442272 100644 --- a/view/component/form/loginform.templ +++ b/view/component/form/loginform.templ @@ -14,7 +14,11 @@ templ LoginForm(loginError string) { } else { errCreds = "false" } - xdata := fmt.Sprintf("{credentialError: %s, errorMessage: '%s'}", errCreds, loginError) + xdata := fmt.Sprintf( + "{credentialError: %s, errorMessage: '%s'}", + errCreds, + loginError, + ) }}