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) } }