From 02db6c719c1cb620964c1a8f4bf347950fd4fbdd Mon Sep 17 00:00:00 2001 From: hobokenchicken Date: Mon, 27 Jul 2026 13:22:43 -0400 Subject: [PATCH] fix(gateway): hash session tokens in WS auth; fix WS reconnect backoff The token-hashing commit (57aec2c) never updated ServeWS to hash tokens before querying the sessions table. Cookie and message-frame auth both compared raw tokens against stored hashes, so every WS connection failed. Also moved reconnectDelay to module scope so the exponential backoff survives across connect() calls, and resets on successful open. --- internal/gateway/client.go | 6 +++--- web/src/stores/ws.ts | 9 +++++---- web/tsconfig.app.tsbuildinfo | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/internal/gateway/client.go b/internal/gateway/client.go index e742363..988f1c6 100644 --- a/internal/gateway/client.go +++ b/internal/gateway/client.go @@ -280,11 +280,11 @@ func ServeWS(db *sql.DB, hub *Hub, logger *slog.Logger, w http.ResponseWriter, r } } - // Test candidates against Postgres sessions + // Test candidates against Postgres sessions (tokens are stored hashed) for _, token := range candidateTokens { err := db.QueryRowContext(context.Background(), `SELECT u.id, u.username FROM sessions s JOIN users u ON u.id = s.user_id WHERE s.token = $1 AND s.expires_at > NOW()`, - token, + hashToken(token), ).Scan(&userID, &username) if err == nil && userID != "" { break @@ -300,7 +300,7 @@ func ServeWS(db *sql.DB, hub *Hub, logger *slog.Logger, w http.ResponseWriter, r if jsonErr := json.Unmarshal(raw, &auth); jsonErr == nil && auth.Token != "" { _ = db.QueryRowContext(context.Background(), `SELECT u.id, u.username FROM sessions s JOIN users u ON u.id = s.user_id WHERE s.token = $1 AND s.expires_at > NOW()`, - auth.Token, + hashToken(auth.Token), ).Scan(&userID, &username) } } diff --git a/web/src/stores/ws.ts b/web/src/stores/ws.ts index 6c5ec91..e6d680d 100644 --- a/web/src/stores/ws.ts +++ b/web/src/stores/ws.ts @@ -66,6 +66,10 @@ function extractIds(payload: UnknownPayload | undefined): { channel_id?: string; return null; } +let reconnectDelay = 1000; +const maxReconnectDelay = 30000; +let reconnectTimeout: number | null = null; + export const useWebSocketStore = create((set, get) => ({ socket: null, connected: false, @@ -78,10 +82,6 @@ export const useWebSocketStore = create((set, get) => ({ const socket = new WebSocket(`${getWsHost()}/ws`); - let reconnectDelay = 1000; - const maxReconnectDelay = 30000; - let reconnectTimeout: number | null = null; - const scheduleReconnect = () => { if (reconnectTimeout) { window.clearTimeout(reconnectTimeout); @@ -95,6 +95,7 @@ export const useWebSocketStore = create((set, get) => ({ }; socket.onopen = () => { + reconnectDelay = 1000; // ponytail: reset backoff on successful connect const token = localStorage.getItem('dumpster_session_token'); if (token) { try { diff --git a/web/tsconfig.app.tsbuildinfo b/web/tsconfig.app.tsbuildinfo index 6d9d7d8..de9410a 100644 --- a/web/tsconfig.app.tsbuildinfo +++ b/web/tsconfig.app.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/App.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/components/AudioRenderers.tsx","./src/components/BotManager.tsx","./src/components/BotStore.tsx","./src/components/CalendarView.tsx","./src/components/ChannelList.tsx","./src/components/ChannelSettingsModal.tsx","./src/components/ChatArea.tsx","./src/components/CommandDropdown.tsx","./src/components/CommandManager.tsx","./src/components/ConnectionStatus.tsx","./src/components/ContextMenu.tsx","./src/components/ConversationList.tsx","./src/components/CreateChannelModal.tsx","./src/components/CreateServerModal.tsx","./src/components/DMChat.tsx","./src/components/DeviceSettingsModal.tsx","./src/components/DocsView.tsx","./src/components/EmojiPicker.tsx","./src/components/ExpandableImage.tsx","./src/components/FeatureRequestsPanel.tsx","./src/components/ForgotPasswordPage.tsx","./src/components/FormatToolbar.tsx","./src/components/ForumView.tsx","./src/components/GiphyPicker.tsx","./src/components/InstallBanner.tsx","./src/components/InstallPrompt.tsx","./src/components/InviteModal.tsx","./src/components/JoinServer.tsx","./src/components/JoinServerModal.tsx","./src/components/Layout.tsx","./src/components/ListView.tsx","./src/components/LoginForm.tsx","./src/components/MemberContextMenu.tsx","./src/components/MemberList.tsx","./src/components/MemberRoleAssign.tsx","./src/components/MentionDropdown.tsx","./src/components/MentionPopup.tsx","./src/components/MessageInput.tsx","./src/components/MessageSearch.tsx","./src/components/MobileDrawer.tsx","./src/components/MobileNav.tsx","./src/components/NewConversationModal.tsx","./src/components/NotificationPrompt.tsx","./src/components/PinnedMessages.tsx","./src/components/Poll.tsx","./src/components/ReactionBar.tsx","./src/components/ReplyBar.tsx","./src/components/ResetPasswordPage.tsx","./src/components/RoleManager.tsx","./src/components/ServerBar.tsx","./src/components/ServerSettingsModal.tsx","./src/components/SlashCommandPopup.tsx","./src/components/ThemeToggle.tsx","./src/components/ThreadListPanel.tsx","./src/components/ThreadPanel.tsx","./src/components/TypingIndicator.tsx","./src/components/UserProfileModal.tsx","./src/components/UserSettings.tsx","./src/components/VideoGrid.tsx","./src/components/VoiceChannel.tsx","./src/components/VoiceControls.tsx","./src/components/VoicePanel.tsx","./src/lib/api.ts","./src/lib/kaomojiData.ts","./src/lib/slashCommands.ts","./src/lib/usePermissions.ts","./src/stores/auth.ts","./src/stores/bot.ts","./src/stores/channel.ts","./src/stores/conversation.ts","./src/stores/featureRequest.ts","./src/stores/layout.ts","./src/stores/member.ts","./src/stores/message.ts","./src/stores/moderation.ts","./src/stores/notificationSettings.ts","./src/stores/permissions.ts","./src/stores/presence.ts","./src/stores/push.ts","./src/stores/readStates.ts","./src/stores/role.ts","./src/stores/server.ts","./src/stores/thread.ts","./src/stores/typing.ts","./src/stores/voice.ts","./src/stores/voicePresence.ts","./src/stores/ws.ts"],"version":"5.9.3"} \ No newline at end of file +{"root":["./src/App.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/components/AudioRenderers.tsx","./src/components/BotManager.tsx","./src/components/BotStore.tsx","./src/components/CalendarView.tsx","./src/components/ChannelList.tsx","./src/components/ChannelSettingsModal.tsx","./src/components/ChatArea.tsx","./src/components/CommandDropdown.tsx","./src/components/CommandManager.tsx","./src/components/ConnectionStatus.tsx","./src/components/ContextMenu.tsx","./src/components/ConversationList.tsx","./src/components/CreateChannelModal.tsx","./src/components/CreateServerModal.tsx","./src/components/DMChat.tsx","./src/components/DeviceSettingsModal.tsx","./src/components/DocsView.tsx","./src/components/EmojiPicker.tsx","./src/components/ExpandableImage.tsx","./src/components/FeatureRequestsPanel.tsx","./src/components/ForgotPasswordPage.tsx","./src/components/FormatToolbar.tsx","./src/components/ForumView.tsx","./src/components/GiphyPicker.tsx","./src/components/InstallBanner.tsx","./src/components/InstallPrompt.tsx","./src/components/InviteModal.tsx","./src/components/JoinServer.tsx","./src/components/JoinServerModal.tsx","./src/components/Layout.tsx","./src/components/ListView.tsx","./src/components/LoginForm.tsx","./src/components/MemberContextMenu.tsx","./src/components/MemberList.tsx","./src/components/MemberRoleAssign.tsx","./src/components/MentionDropdown.tsx","./src/components/MentionPopup.tsx","./src/components/MessageInput.tsx","./src/components/MessageSearch.tsx","./src/components/MobileDrawer.tsx","./src/components/MobileNav.tsx","./src/components/NewConversationModal.tsx","./src/components/NotificationPrompt.tsx","./src/components/PinnedMessages.tsx","./src/components/Poll.tsx","./src/components/ReactionBar.tsx","./src/components/ReplyBar.tsx","./src/components/ResetPasswordPage.tsx","./src/components/RoleManager.tsx","./src/components/ServerBar.tsx","./src/components/ServerSettingsModal.tsx","./src/components/SlashCommandPopup.tsx","./src/components/ThemeToggle.tsx","./src/components/ThreadListPanel.tsx","./src/components/ThreadPanel.tsx","./src/components/TypingIndicator.tsx","./src/components/UserProfileModal.tsx","./src/components/UserSettings.tsx","./src/components/VersionNotifier.tsx","./src/components/VideoGrid.tsx","./src/components/VoiceChannel.tsx","./src/components/VoiceControls.tsx","./src/components/VoicePanel.tsx","./src/lib/api.ts","./src/lib/kaomojiData.ts","./src/lib/slashCommands.ts","./src/lib/usePermissions.ts","./src/stores/auth.ts","./src/stores/bot.ts","./src/stores/channel.ts","./src/stores/conversation.ts","./src/stores/featureRequest.ts","./src/stores/layout.ts","./src/stores/member.ts","./src/stores/message.ts","./src/stores/moderation.ts","./src/stores/notificationSettings.ts","./src/stores/permissions.ts","./src/stores/presence.ts","./src/stores/push.ts","./src/stores/readStates.ts","./src/stores/role.ts","./src/stores/server.ts","./src/stores/thread.ts","./src/stores/typing.ts","./src/stores/voice.ts","./src/stores/voicePresence.ts","./src/stores/ws.ts"],"version":"5.9.3"} \ No newline at end of file