From cadb42d17aeae1d3232924c6cfdd9621ab0f4f1f Mon Sep 17 00:00:00 2001 From: Alexey Date: Sat, 3 Jan 2026 15:42:29 +0200 Subject: [PATCH] add logout method --- api/auth/logout.go | 82 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 api/auth/logout.go diff --git a/api/auth/logout.go b/api/auth/logout.go new file mode 100644 index 0000000..4216d3b --- /dev/null +++ b/api/auth/logout.go @@ -0,0 +1,82 @@ +package api_auth + +import ( + "errors" + "fmt" + "log/slog" + "net/http" + + "git.oblat.lv/alex/triggerssmith/internal/auth" + "git.oblat.lv/alex/triggerssmith/internal/server" + "github.com/golang-jwt/jwt/v5" +) + +// @Summary Logout +// @Description Requires valid refresh token +// @Tags auth +// @Success 204 +// @Failure 401 {object} server.ProblemDetails +// @Failure 500 {object} server.ProblemDetails +// @Router /api/auth/logout [post] +func (h *authHandler) handleLogout(w http.ResponseWriter, r *http.Request) { + // claims, err := h.a.AuthenticateRequest(r) + // if err != nil { + // slog.Error("failed to AuthenticateRequest", "error", err.Error()) + // switch err { + // case auth.ErrInvalidToken: + // server.WriteProblem(w, http.StatusUnauthorized, "/errors/auth/invalid-token", "Invalid token", "Invalid token: taking cookies anyways", r) + // case auth.ErrTokenIsMissing: + // server.WriteProblem(w, http.StatusUnauthorized, "/errors/auth/invalid-token", "Invalid token", "Token is missing: taking cookies anyway", r) + // default: + // server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error: taking cookies anyway", r) + // } + // http.SetCookie(w, &http.Cookie{ + // Name: "refresh_token", + // Value: "", + // MaxAge: -1, + // Path: "/api/auth/", + // HttpOnly: true, + // SameSite: http.SameSiteLaxMode, + // }) + // return + // } + // rjti := claims.(jwt.MapClaims)["rjti"].(string) + refreshCookie, err := r.Cookie("refresh_token") + if err != nil && errors.Is(err, http.ErrNoCookie) { + server.WriteProblem(w, http.StatusUnauthorized, "/errors/auth/refresh-token-not-found", "Refresh token is missing", "Refresh token is missing", r) + return + } + refreshStr := refreshCookie.Value + if refreshStr == "" { + server.WriteProblem(w, http.StatusUnauthorized, "/errors/auth/refresh-token-not-found", "Refresh token is missing", "Refresh token is missing", r) + return + } + claims, err := h.a.ValidateRefreshToken(refreshStr) + if err != nil { + slog.Error("failed to ValidateRefreshToken", "error", err.Error()) + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error while validating refresh token: maybe invalid", r) + return + } + rjti := claims.(jwt.MapClaims)["jti"].(string) + err = h.a.Logout(rjti) + if err != nil { + slog.Error("failed to Logout", "error", err.Error()) + switch err { + case auth.ErrInvalidToken: + server.WriteProblem(w, http.StatusUnauthorized, "/errors/auth/already-revoked", "Token already revoked", fmt.Sprintf("Token with rjti '%s' is already revoked", rjti), r) + default: + server.WriteProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error: taking cookies anyway", r) + } + } + http.SetCookie(w, &http.Cookie{ + Name: "refresh_token", + Value: "", + MaxAge: -1, + Path: "/api/auth/", + HttpOnly: true, + SameSite: http.SameSiteLaxMode, + }) + if err == nil { + w.WriteHeader(http.StatusNoContent) + } +}