# Memos Frontend Documentation ## Authentication and Middleware System ### Frontend Authentication Architecture The frontend authentication system uses a combination of: 1. **React Context** for state management 2. **Route-based guards** for access control 3. **Connect RPC interceptors** for API authentication 4. **Layout-based protection** for UI components ### Core Authentication Components #### 1. Auth Context (`web/src/contexts/AuthContext.tsx`) ```typescript // Manages global authentication state const AuthContext = createContext({ currentUser: undefined, userGeneralSetting: undefined, isInitialized: false, isLoading: true, initialize: async () => {}, logout: () => {}, refetchSettings: async () => {} }); // Provides authentication state to entire app function AuthProvider({ children }: { children: React.ReactNode }) { // Handles token initialization, user fetching, logout logic } ``` #### 2. Route Protection System **Public Routes** (no auth required): - `/auth` - Authentication pages - `/auth/signup` - User registration - `/auth/callback` - OAuth callback - `/explore` - Public explore page - `/u/:username` - User profiles - `/memos/:uid` - Individual memo details **Private Routes** (auth required): - `/` (root) - Main dashboard - `/attachments` - File attachments - `/inbox` - Notifications - `/archived` - Archived memos - `/setting` - User settings #### 3. Layout-Based Authentication Guards **RootLayout** (`web/src/layouts/RootLayout.tsx`): ```typescript const RootLayout = () => { const currentUser = useCurrentUser(); useEffect(() => { if (!currentUser) { redirectOnAuthFailure(); // Redirects to /auth } }, [currentUser]); // Renders navigation and main content return (
); }; ``` **Route Configuration** (`web/src/router/index.tsx`): ```typescript const router = createBrowserRouter([ { path: "/", element: , // Handles instance initialization children: [ { path: "/auth", // Public routes children: [ { path: "", element: }, { path: "signup", element: }, { path: "callback", element: } ] }, { path: "/", // Protected routes (wrapped in RootLayout) element: , // Auth guard here children: [ { element: , // Main app layout children: [ { path: "", element: }, { path: "explore", element: } // ... other protected routes ] } ] } ] } ]); ``` ### API Authentication Interceptors #### Connect RPC Interceptor (`web/src/connect.ts`): ```typescript const authInterceptor: Interceptor = (next) => async (req) => { const isRetryAttempt = req.header.get(RETRY_HEADER) === RETRY_HEADER_VALUE; const token = await getRequestToken(); setAuthorizationHeader(req, token); try { return await next(req); } catch (error) { // Handle 401 Unauthorized if (error.code === Code.Unauthenticated && !isRetryAttempt) { try { // Attempt token refresh const newToken = await refreshAndGetAccessToken(); setAuthorizationHeader(req, newToken); req.header.set(RETRY_HEADER, RETRY_HEADER_VALUE); return await next(req); } catch (refreshError) { redirectOnAuthFailure(); // Redirect to login throw refreshError; } } throw error; } }; ``` #### Token Management - **Storage**: Access tokens stored in memory (not localStorage) - **Refresh**: Automatic token refresh on 401 errors - **Expiration**: Proactive refresh on tab focus - **Cleanup**: Tokens cleared on logout/auth failure ### Authentication Flow #### 1. App Initialization (`web/src/main.tsx`) ```typescript // Early theme/locale setup to prevent flash applyThemeEarly(); applyLocaleEarly(); // Initialize auth and instance contexts ``` #### 2. Authentication Check (`web/src/utils/auth-redirect.ts`) ```typescript export function redirectOnAuthFailure(): void { const currentPath = window.location.pathname; // Allow public routes if (isPublicRoute(currentPath)) return; // Redirect private routes to auth if (isPrivateRoute(currentPath)) { clearAccessToken(); window.location.replace(ROUTES.AUTH); } } ``` #### 3. User Session Management - Token refresh on window focus - Automatic logout on token expiration - Context cleanup on logout - Query cache invalidation ### Key Security Features - **Token Storage**: In-memory only (security best practice) - **Automatic Refresh**: Handles token rotation seamlessly - **Route Guards**: Prevent unauthorized access to protected routes - **Context Isolation**: Auth state managed centrally - **Error Handling**: Graceful degradation on auth failures - **Session Cleanup**: Complete state reset on logout ### Related Files - `web/src/contexts/AuthContext.tsx` - Authentication state management - `web/src/layouts/RootLayout.tsx` - Main authentication guard - `web/src/utils/auth-redirect.ts` - Route protection logic - `web/src/connect.ts` - API authentication interceptors - `web/src/router/index.tsx` - Route configuration - `web/src/main.tsx` - App initialization ## Theme System Implementation ### Vite Build Process for Themes The CSS themes are processed and built by **Vite** during the build process: 1. **Vite Configuration** (`web/vite.config.mts`) - Uses `@tailwindcss/vite` plugin for CSS processing - Tailwind CSS v4 handles theme token compilation - Themes are bundled during `pnpm build` process 2. **CSS Processing Pipeline** - Base styles imported in `web/src/index.css` - Theme-specific CSS files located in `web/src/themes/` - Vite processes `@theme inline` directives at build time - Dynamic theme switching handled via JavaScript at runtime ### Theme Architecture #### Core Theme Files - `web/src/index.css` - Main CSS entry point - `web/src/themes/default.css` - Base theme with Tailwind token mappings - `web/src/themes/default-dark.css` - Dark theme variables - `web/src/themes/paper.css` - Paper-style theme - `web/src/utils/theme.ts` - Theme loading and management logic #### How Themes Are Built ##### 1. Build Time Processing (Vite + Tailwind) ```css /* In web/src/themes/default.css */ @theme inline { --color-background: var(--background); --color-foreground: var(--foreground); /* ... other CSS variables */ } ``` - Tailwind compiles these into static CSS classes - Shared across all themes - Optimized during Vite build process ##### 2. Runtime Theme Switching - JavaScript dynamically injects theme CSS - Uses `?raw` import to get CSS as string - Injects `