From 7e8b65b36dce243babeefe27ef1739cb4c495beb Mon Sep 17 00:00:00 2001 From: MaheshtheDev <38828053+MaheshtheDev@users.noreply.github.com> Date: Wed, 24 Sep 2025 21:02:05 +0000 Subject: feat(extension): Auto Logout when token is invalidated, Account Section (#438) ## Changes: - Added token validation logic that checks if the current auth token is still valid - Implemented automatic logout when token is invalidated with appropriate user feedback - Added an Account section in the popup that displays the user's email - Improved error toast messages with clearer formatting and helper text ![image.png](https://app.graphite.dev/user-attachments/assets/b751f6a4-1a33-42d1-88dd-61aff55dd1c7.png)![image.png](https://app.graphite.dev/user-attachments/assets/202f69f9-c853-4801-850e-ee6d115e300c.png) --- apps/browser-extension/entrypoints/popup/App.tsx | 108 +++++++++++++++++++---- apps/browser-extension/utils/api.ts | 29 ++++++ apps/browser-extension/utils/query-hooks.ts | 12 +++ apps/browser-extension/utils/ui-components.ts | 52 +++++++++-- apps/browser-extension/wxt.config.ts | 2 +- 5 files changed, 174 insertions(+), 29 deletions(-) diff --git a/apps/browser-extension/entrypoints/popup/App.tsx b/apps/browser-extension/entrypoints/popup/App.tsx index 3e4f15e2..c6e8468c 100644 --- a/apps/browser-extension/entrypoints/popup/App.tsx +++ b/apps/browser-extension/entrypoints/popup/App.tsx @@ -1,11 +1,13 @@ import { useQueryClient } from "@tanstack/react-query" import { useEffect, useState } from "react" import "./App.css" +import { validateAuthToken } from "../../utils/api" import { MESSAGE_TYPES, STORAGE_KEYS } from "../../utils/constants" import { useDefaultProject, useProjects, useSetDefaultProject, + useUserData, } from "../../utils/query-hooks" import type { Project } from "../../utils/types" @@ -20,6 +22,7 @@ function App() { "save", ) const [autoSearchEnabled, setAutoSearchEnabled] = useState(false) + const [authInvalidated, setAuthInvalidated] = useState(false) const queryClient = useQueryClient() const { data: projects = [], isLoading: loadingProjects } = useProjects({ @@ -28,8 +31,12 @@ function App() { const { data: defaultProject } = useDefaultProject({ enabled: userSignedIn, }) + const { data: userData, isLoading: loadingUserData } = useUserData({ + enabled: userSignedIn, + }) const setDefaultProjectMutation = useSetDefaultProject() + // biome-ignore lint/correctness/useExhaustiveDependencies: suppress dependency analysis useEffect(() => { const checkAuthStatus = async () => { try { @@ -37,8 +44,28 @@ function App() { STORAGE_KEYS.BEARER_TOKEN, STORAGE_KEYS.AUTO_SEARCH_ENABLED, ]) - const isSignedIn = !!result[STORAGE_KEYS.BEARER_TOKEN] - setUserSignedIn(isSignedIn) + const hasToken = !!result[STORAGE_KEYS.BEARER_TOKEN] + + if (hasToken) { + const isTokenValid = await validateAuthToken() + + if (isTokenValid) { + setUserSignedIn(true) + setAuthInvalidated(false) + } else { + await chrome.storage.local.remove([ + STORAGE_KEYS.BEARER_TOKEN, + STORAGE_KEYS.USER_DATA, + STORAGE_KEYS.DEFAULT_PROJECT, + ]) + queryClient.clear() + setUserSignedIn(false) + setAuthInvalidated(true) + } + } else { + setUserSignedIn(false) + setAuthInvalidated(false) + } const autoSearchSetting = result[STORAGE_KEYS.AUTO_SEARCH_ENABLED] ?? false @@ -46,6 +73,7 @@ function App() { } catch (error) { console.error("Error checking auth status:", error) setUserSignedIn(false) + setAuthInvalidated(false) } finally { setLoading(false) } @@ -397,6 +425,34 @@ function App() { ) : (
+ {/* Account Section */} +
+

+ Account +

+
+ {loadingUserData ? ( +
+ Loading account data... +
+ ) : userData?.email ? ( +
+ + Email + + + {userData.email} + +
+ ) : ( +
+ No email found +
+ )} +
+
+ + {/* Chat Integration Section */}

Chat Integration @@ -480,23 +536,37 @@ function App() {

) : (
-
-

- Login to unlock all chrome extension features -

- -
    -
  • - Save any page to your supermemory -
  • -
  • - Import all your Twitter / X Bookmarks -
  • -
  • - Import your ChatGPT Memories -
  • -
-
+ {authInvalidated ? ( +
+
+

+ Session Expired +

+

+ Logged out since authentication was invalidated. Please + login again. +

+
+
+ ) : ( +
+

+ Login to unlock all chrome extension features +

+ +
    +
  • + Save any page to your supermemory +
  • +
  • + Import all your Twitter / X Bookmarks +
  • +
  • + Import your ChatGPT Memories +
  • +
+
+ )}

diff --git a/apps/browser-extension/utils/api.ts b/apps/browser-extension/utils/api.ts index 2a4c838b..d98b39f5 100644 --- a/apps/browser-extension/utils/api.ts +++ b/apps/browser-extension/utils/api.ts @@ -99,6 +99,35 @@ export async function setDefaultProject(project: Project): Promise { } } +/** + * Validate if current bearer token is still valid + */ +export async function validateAuthToken(): Promise { + try { + await makeAuthenticatedRequest("/v3/projects") + return true + } catch (error) { + if (error instanceof AuthenticationError) { + return false + } + console.error("Failed to validate auth token:", error) + return true + } +} + +/** + * Get user data from storage + */ +export async function getUserData(): Promise<{ email?: string } | null> { + try { + const result = await chrome.storage.local.get([STORAGE_KEYS.USER_DATA]) + return result[STORAGE_KEYS.USER_DATA] || null + } catch (error) { + console.error("Failed to get user data:", error) + return null + } +} + /** * Save memory to Supermemory API */ diff --git a/apps/browser-extension/utils/query-hooks.ts b/apps/browser-extension/utils/query-hooks.ts index 721a68ad..5c7cfc21 100644 --- a/apps/browser-extension/utils/query-hooks.ts +++ b/apps/browser-extension/utils/query-hooks.ts @@ -5,6 +5,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query" import { fetchProjects, getDefaultProject, + getUserData, saveMemory, searchMemories, setDefaultProject, @@ -15,6 +16,7 @@ import type { MemoryPayload } from "./types" export const queryKeys = { projects: ["projects"] as const, defaultProject: ["defaultProject"] as const, + userData: ["userData"] as const, } // Projects Query @@ -37,6 +39,16 @@ export function useDefaultProject(options?: { enabled?: boolean }) { }) } +// User Data Query +export function useUserData(options?: { enabled?: boolean }) { + return useQuery({ + queryKey: queryKeys.userData, + queryFn: getUserData, + staleTime: 5 * 60 * 1000, // 5 minutes + enabled: options?.enabled ?? true, + }) +} + // Set Default Project Mutation export function useSetDefaultProject() { const queryClient = useQueryClient() diff --git a/apps/browser-extension/utils/ui-components.ts b/apps/browser-extension/utils/ui-components.ts index 8a56ea5a..dabe4974 100644 --- a/apps/browser-extension/utils/ui-components.ts +++ b/apps/browser-extension/utils/ui-components.ts @@ -94,8 +94,8 @@ export function createToast(state: ToastState): HTMLElement { const icon = document.createElement("div") icon.style.cssText = "width: 20px; height: 20px; flex-shrink: 0;" - const text = document.createElement("span") - text.style.fontWeight = "500" + let textElement: HTMLElement = document.createElement("span") + textElement.style.fontWeight = "500" // Configure toast based on state switch (state) { @@ -113,17 +113,17 @@ export function createToast(state: ToastState): HTMLElement { ` icon.style.animation = "spin 1s linear infinite" - text.textContent = "Adding to Memory..." + textElement.textContent = "Adding to Memory..." break case "success": { const iconUrl = browser.runtime.getURL("/icon-16.png") icon.innerHTML = `Success` - text.textContent = "Added to Memory" + textElement.textContent = "Added to Memory" break } - case "error": + case "error": { icon.innerHTML = ` @@ -131,12 +131,29 @@ export function createToast(state: ToastState): HTMLElement { ` - text.textContent = "Failed to save memory / Make sure you are logged in" + const textContainer = document.createElement("div") + textContainer.style.cssText = + "display: flex; flex-direction: column; gap: 2px;" + + const mainText = document.createElement("span") + mainText.style.cssText = "font-weight: 500; line-height: 1.2;" + mainText.textContent = "Failed to save memory" + + const helperText = document.createElement("span") + helperText.style.cssText = + "font-size: 12px; color: #6b7280; font-weight: 400; line-height: 1.2;" + helperText.textContent = "Make sure you are logged in" + + textContainer.appendChild(mainText) + textContainer.appendChild(helperText) + + textElement = textContainer break + } } toast.appendChild(icon) - toast.appendChild(text) + toast.appendChild(textElement) return toast } @@ -433,8 +450,25 @@ export const DOMUtils = { ` icon.style.animation = "" - text.textContent = - "Failed to save memory / Make sure you are logged in" + + const textContainer = document.createElement("div") + textContainer.style.cssText = + "display: flex; flex-direction: column; gap: 2px;" + + const mainText = document.createElement("span") + mainText.style.cssText = "font-weight: 500; line-height: 1.2;" + mainText.textContent = "Failed to save memory" + + const helperText = document.createElement("span") + helperText.style.cssText = + "font-size: 12px; color: #6b7280; font-weight: 400; line-height: 1.2;" + helperText.textContent = "Make sure you are logged in" + + textContainer.appendChild(mainText) + textContainer.appendChild(helperText) + + text.innerHTML = "" + text.appendChild(textContainer) } // Auto-dismiss diff --git a/apps/browser-extension/wxt.config.ts b/apps/browser-extension/wxt.config.ts index 1bd7910d..b536b853 100644 --- a/apps/browser-extension/wxt.config.ts +++ b/apps/browser-extension/wxt.config.ts @@ -11,7 +11,7 @@ export default defineConfig({ manifest: { name: "supermemory", homepage_url: "https://supermemory.ai", - version: "6.0.002", + version: "6.0.003", permissions: ["contextMenus", "storage", "activeTab", "webRequest", "tabs"], host_permissions: [ "*://x.com/*", -- cgit v1.2.3