Authentication and session management
Introduction#
WhatsApp QR login with LocalAuth, session reuse, disconnect handling, and clearing auth/cache when logout is forced.
Project structure#
The authentication and session management spans three layers:
- Renderer UI (React): Presents the WhatsApp interface, displays QR code, and shows status/log.
- Preload bridge: Exposes secure IPC methods to the renderer for WhatsApp operations.
- Main process: Runs the whatsapp-web.js client with LocalAuth, handles events, and manages sessions.
graph TB UI["WhatsAppForm.jsx<br/>Renderer UI"] --> Bridge["preload.js<br/>IPC Bridge"] Bridge --> Main["main.js<br/>Main Process"] Main --> WWeb["whatsapp-web.js Client<br/>LocalAuth"] Main --> QR["qrcode Library<br/>Generate QR Data URL"] Main --> FS["File System<br/>Delete .wwebjs_cache/.wwebjs_auth"] Main --> Events["Event Handlers<br/>qr, ready, authenticated,<br/>auth_failure, disconnected"]
Core components#
- WhatsApp client initialization and event handling in the main process using LocalAuth.
- Renderer UI that renders QR code, shows status, and triggers actions.
- IPC bridge exposing WhatsApp operations to the renderer.
- Session cleanup utilities to remove cached authentication and browser data.
Key responsibilities:
- Initialize client with LocalAuth and puppeteer options.
- Emit status updates for QR display, ready, authenticated, auth failure, and disconnection.
- Convert QR string to a data URL for rendering in the UI.
- Persist sessions locally via LocalAuth and clear them on logout or app close.
- Provide reliable error handling and user feedback.
Architecture overview#
The system uses a secure IPC model:
- The renderer invokes startWhatsAppClient via preload.js.
- The main process creates a Client with LocalAuth and registers event listeners.
- QR codes are generated as data URLs and sent to the renderer.
- On ready or authenticated, the QR is cleared and status indicates success.
- Logout and app lifecycle hooks trigger cleanup of session files.
sequenceDiagram
participant UI as "WhatsAppForm.jsx"
participant Bridge as "preload.js"
participant Main as "main.js"
participant WWeb as "whatsapp-web.js Client"
participant QRlib as "qrcode Library"
UI->>Bridge : invoke("whatsapp-start-client")
Bridge->>Main : ipcRenderer.invoke("whatsapp-start-client")
Main->>WWeb : new Client(LocalAuth, puppeteer options)
WWeb-->>Main : "qr" event (qr string)
Main->>QRlib : toDataURL(qr)
QRlib-->>Main : data URL
Main-->>UI : emit("whatsapp-qr", data URL)
WWeb-->>Main : "ready" or "authenticated"
Main-->>UI : emit("whatsapp-qr", null)
Main-->>UI : emit("whatsapp-status", "Client is ready!" / "Authenticated!")
UI->>Bridge : invoke("whatsapp-logout")
Bridge->>Main : ipcRenderer.invoke("whatsapp-logout")
Main->>WWeb : logout()
Main->>Main : deleteWhatsAppFiles()
Main-->>UI : emit("whatsapp-status", "Disconnected")Detailed component analysis#
QR code authentication flow (LocalAuth)#
- Initialization: The main process creates a Client with LocalAuth and headless puppeteer options.
- QR Generation: On receiving the “qr” event, the main process converts the QR string to a data URL using the qrcode library and sends it to the renderer.
- UI Rendering: The renderer displays the QR code image and sets status to “Scan QR code to authenticate”.
- Completion: On “ready” or “authenticated”, the main process clears the QR and updates status to indicate success.
flowchart TD
Start(["User clicks Connect"]) --> Init["Initialize Client with LocalAuth"]
Init --> WaitQR["Wait for 'qr' event"]
WaitQR --> GenQR["Convert QR string to Data URL"]
GenQR --> RenderQR["Send QR to Renderer"]
RenderQR --> ShowUI["Display QR in UI"]
ShowUI --> AuthEvents{"Ready or Authenticated?"}
AuthEvents --> |Ready| ClearQR["Clear QR and set status 'Client is ready!'"]
AuthEvents --> |Authenticated| ClearQR
ClearQR --> Success(["Authenticated"])Client lifecycle management#
- Startup: Immediate status update indicates initialization, followed by client start.
- Ready State: Emitted when the client is fully initialized and ready to send messages.
- Authentication Failure: Emitted when authentication fails; status includes the failure message.
- Disconnection: Emitted on disconnect; client reference is cleared and status updated.
- Shutdown: On app close or before-quit, the main process attempts logout and deletes session files.
stateDiagram-v2 [*] --> Initializing Initializing --> WaitingForQR : "qr event" WaitingForQR --> Scanning : "display QR" Scanning --> Authenticated : "authenticated" Scanning --> Ready : "ready" Ready --> Authenticated : "authenticated" Authenticated --> Disconnected : "disconnected" Disconnected --> Initializing : "reconnect" Initializing --> Error : "auth_failure" Error --> Initializing : "retry"
Session persistence and cache management#
- LocalAuth Strategy: Persists authentication state locally so subsequent runs can reconnect without scanning a QR.
- Cache Cleanup: On logout and app close/quit, the main process deletes the .wwebjs_cache and .wwebjs_auth directories to force re-authentication and clear stale session data.
- Automatic Reconnection: Subsequent starts reuse persisted credentials until invalidated by logout or cache deletion.
flowchart TD
Start(["App Starts"]) --> CheckCache["Check for .wwebjs_cache/.wwebjs_auth"]
CheckCache --> HasCache{"Cache Exists?"}
HasCache --> |Yes| AutoReconnect["Attempt to reconnect with LocalAuth"]
HasCache --> |No| FreshAuth["Start fresh authentication (QR)"]
AutoReconnect --> AuthResult{"Authenticated?"}
AuthResult --> |Yes| Ready["Client Ready"]
AuthResult --> |No| FreshAuth
FreshAuth --> QR["Display QR for new session"]
Ready --> Running(["Running"])
QR --> RunningRenderer integration and user experience#
- Status Updates: The renderer listens to “whatsapp-status” and “whatsapp-qr” events to reflect current state.
- QR Handling: The renderer displays QR when present and handles load/error states.
- Logout UX: Provides a logout button and clears UI state upon success.
sequenceDiagram
participant Renderer as "WhatsAppForm.jsx"
participant Bridge as "preload.js"
participant Main as "main.js"
Renderer->>Bridge : on("whatsapp-status", handler)
Renderer->>Bridge : on("whatsapp-qr", handler)
Main-->>Renderer : emit("whatsapp-status", "...") x N
Main-->>Renderer : emit("whatsapp-qr", dataURL or null)
Renderer->>Renderer : Update UI state (status, QR, buttons)Contact import and message composition (supporting components)#
- Manual Number Parsing: Uses Pyodide to run Python scripts for parsing and validating manual numbers.
- Contact Extraction: Python backend utilities extract and normalize contacts from CSV/Excel/Text files.
- Message Personalization: The renderer supports placeholders for personalization.
graph TB UI["WhatsAppForm.jsx"] --> PMN["pyodide.js<br/>parseManualNumbers()"] PMN --> PyParse["parse_manual_numbers.py"] UI --> Extract["extract_contacts.py"] UI --> Validate["validate_number.py"] UI --> Send["sendWhatsAppBulk()<br/>main.js"]
Dependency analysis#
External libraries and their roles:
- whatsapp-web.js: Provides the WhatsApp client with LocalAuth strategy.
- qrcode: Converts QR strings to data URLs for rendering.
- fs: Used to delete session cache directories on logout and app lifecycle events.
- csv-parser: Parses CSV contact files in the main process.
graph TB Main["main.js"] --> WWeb["whatsapp-web.js"] Main --> QRlib["qrcode"] Main --> FS["fs"] Main --> CSV["csv-parser"] UI["WhatsAppForm.jsx"] --> Bridge["preload.js"] Bridge --> Main
Performance considerations#
- Headless browser: Puppeteer runs headless to reduce overhead; ensure sufficient system resources.
- Delays between sends: The main process introduces deliberate delays to avoid rate limits and improve reliability.
- QR generation: Converting QR strings to data URLs is lightweight but avoid excessive regeneration.
- Cleanup: Deleting cache and auth directories prevents accumulation of stale data.
Troubleshooting guide#
Common issues and resolutions:
QR code not loading
Symptoms: QR image shows as failed or blank.
Causes: Network issues, QR generation errors, renderer image load failure.
Resolution: Retry connection; check console for QR generation errors; ensure network connectivity.
Related code: QR error handling and retry button in the renderer.
Authentication failure
Symptoms: Status indicates “Authentication failed”.
Causes: Invalid QR, corrupted session cache, or authentication timeout.
Resolution: Clear session cache and retry; ensure device is linked; check logs for detailed messages.
Related code: auth_failure event handler and status emission.
Disconnection
Symptoms: Status indicates “Client disconnected”.
Causes: Network issues, browser crash, or external logout.
Resolution: Reconnect by initiating authentication again; ensure stable network.
Related code: disconnected event handler and client cleanup.
Session corruption or stale cache
Symptoms: Repeated authentication failures despite valid credentials.
Resolution: Force logout to clear cache and auth directories; restart the client.
Related code: logout handler and deleteWhatsAppFiles().
Application lifecycle cleanup
Symptoms: Old session persists after app close.
Resolution: Rely on app/window close and before-quit handlers to logout and delete files.
Related code: app lifecycle hooks and deleteWhatsAppFiles().
Security considerations#
- Local authentication storage
- LocalAuth stores session data locally; protect the application directory and avoid sharing it.
- Consider restricting file permissions on the .wwebjs_cache and .wwebjs_auth directories.
- Session cleanup
- Always call logout and delete session files when switching users or ending a session.
- On app close/quit, ensure cleanup routines run to prevent accidental reuse of stale sessions.
- Input validation
- Validate and sanitize contact inputs to avoid injection or malformed data.
- Least privilege
- Run the application with minimal required privileges; avoid unnecessary filesystem access outside designated areas.
Conclusion#
QR not loading is usually network or a stuck Chromium profile. Clear auth/cache, restart client, scan again.