๐ฃ๏ธ User Feedback¶
๐ Overview¶
Architecture of the Give Feedback feature powered by QuackBack.
Secure SSO token-based authentication, automatic theme sync, responsive design, and outside-click dismissal. Migration-ready architecture using internal user IDs instead of vendor-specific identifiers.
๐๏ธ Architecture¶
Design Principles¶
- Migration-Proof Identity โ Internal ULID-based user IDs (not Clerk IDs)
- Secure SSO Authentication โ HMAC-SHA256 signed JWT tokens
- Zero UI Changes โ Single "Give Feedback" button interface
- Progressive Enhancement โ Graceful degradation if SDK unavailable
- Theme Consistency โ Auto-syncs with app light/dark mode
System Flow¶
Frontend (Clerk) โ Backend (/api/widget-sso) โ QuackBack Widget
Get JWT โ Request SSO โ Sign Token โ Identify User โ Pre-fill Form
๐ SSO Authentication¶
Why SSO?¶
| Benefit | Description |
|---|---|
| Security | Prevents user impersonation via cryptographic signatures |
| Trust | Backend-signed tokens verified by QuackBack |
| No Tampering | Browser cannot fake user identity |
Token Format¶
HMAC-SHA256 signed JWT with 5-minute expiration containing internal userId (not Clerk ID)
๐ How It Works¶
1. SDK Initialization¶
- SDK loaded asynchronously from
https://together.visualli.ai/api/widget/sdk.js - Widget initializes with hidden default launcher (custom button used)
- Initial theme detected from localStorage or system preference
- Responsive sizing: 450ร500px desktop, 70dvh mobile drawer
2. SSO Token Request¶
QuackbackIdentifymonitors Clerk authentication state- On login, requests SSO token from
/api/widget-sso - Backend validates Clerk JWT, fetches user from MongoDB
- Returns signed token with internal userId, email, name
3. User Authentication¶
- Frontend authenticates widget with SSO token
- QuackBack verifies HMAC signature using shared secret
- Widget pre-fills user data (email, name)
- Feedback submissions automatically associated with user
4. Theme & UI Sync¶
QuackbackIdentifywatches ThemeContext changes- Updates widget via DOM class manipulation + CSS overrides
- Responsive sizing enforced via CSS + MutationObserver
- Outside-click handler closes widget when clicking outside bounds
5. Feedback Submission¶
- User clicks "Give Feedback" button
- Widget opens with user context pre-filled (if authenticated)
- User submits text feedback through QuackBack interface
- QuackBack backend handles storage and processing
๐๏ธ Database & Identity¶
Dual ID System¶
| ID Type | Purpose | Format |
|---|---|---|
| Clerk ID | Authentication only (not sent to QuackBack) | Standard Clerk format |
| Internal ID | Canonical identifier, migration-ready | ULID format |
Name Resolution Strategy¶
MongoDB user record โ Clerk API โ Email prefix โ "User" fallback
This decoupling enables migration to different auth providers without changing QuackBack data.
๐๏ธ Responsibilities¶
| Component | Responsibilities |
|---|---|
| Frontend | Loads SDK asynchronously Renders "Give Feedback" button Requests SSO tokens from backend Authenticates widget Syncs theme via DOM classes Enforces responsive sizing Handles outside-click dismissal |
| Backend | Validates Clerk JWT (Bearer auth) Fetches user from MongoDB Resolves user name Signs SSO tokens with HMAC-SHA256 Requires QUACKBACK_WIDGET_SECRET env variable |
| QuackBack | Verifies SSO token signatures Stores feedback submissions Associates feedback with internal user IDs Provides dashboard and analytics |
โ๏ธ Key Design Decisions¶
| Decision | Reason | Trade-off |
|---|---|---|
| Internal User IDs | Decouples from auth provider, migration-ready | None |
| SSO Token Authentication | Prevents user impersonation, secure identity verification | Requires backend endpoint |
| Database Call for userId | Need internal ID for migration-proof identity | One query per session |
| Name from Clerk API | No schema change needed, OAuth has latest data | Extra API call (~100ms) |
| Hidden Default Launcher | Uses custom button for consistent UI/UX | None |
| CSS + MutationObserver | Robust responsive sizing, works if SDK changes markup | Slightly more complex |
| DOM Class Theme Sync | SDK may not support runtime config updates | Requires !important CSS rules |
| Async SDK Loading | Non-blocking, doesn't impact initial page render | Must check availability |
๐จ UI Customization¶
| Feature | Desktop | Mobile | Implementation |
|---|---|---|---|
| Theme Sync | Light/Dark | Light/Dark | DOM classes + CSS !important rules |
| Widget Size | 450ร500px bottom-right | 70dvh full-width drawer | CSS + MutationObserver |
| Outside Click | Closes on outside click | Closes on outside click | Click handler checks bounds |
๐ฆ Integration Components¶
Backend¶
/api/widget-sso/route.js โ SSO token generation endpoint
Frontend¶
| File | Purpose |
|---|---|
global.d.ts |
TypeScript declarations for QuackBack SDK |
index.html |
SDK loader + CSS overrides |
QuackbackIdentify.tsx |
SSO authentication, theme sync, sizing, outside-click handler |
FeedbackButton.tsx |
Widget trigger with availability checks |
๐ Security¶
Trust Model: Backend signs โ QuackBack verifies โ Trusts data
| Concern | Mitigation |
|---|---|
| User impersonation | HMAC-SHA256 signed tokens verified by QuackBack |
| Token expiry | 5-minute expiration window |
| Secret exposure | Server-side secret, never sent to client |
| Unauthorized API access | Clerk JWT Bearer auth required |
| Data exposure | Only email, name, and internal userId sent |
| SDK availability | Defensive checks, graceful degradation |
| Script injection | SDK loaded from trusted domain |
๐ Migration Path¶
Switching Auth Providers: Update auth middleware only โ no QuackBack data migration needed (internal IDs remain stable).
Example: Clerk โ Auth0 migration requires updating backend token validation, but all existing feedback remains associated with same internal user IDs.
๐ User Flows¶
Authenticated Flow¶
User logs in via Clerk โ QuackbackIdentify detects authentication โ Requests SSO token โ Backend validates & signs token โ Frontend authenticates widget โ User clicks "Give Feedback" โ Widget opens with email/name pre-filled โ Feedback submitted with internal userId
Anonymous Flow¶
User not logged in โ Clicks "Give Feedback" โ Widget opens without pre-filled data โ Submits feedback anonymously
Theme Change Flow¶
User toggles theme โ ThemeContext updates โ QuackbackIdentify detects change โ Applies DOM classes โ CSS overrides enforce visual consistency
๐ Performance¶
Total Latency: ~200ms first call
| Component | Time | Notes |
|---|---|---|
| SDK Load | ~50ms | Async, non-blocking |
| MongoDB Query | ~20ms | Fetch user by Clerk ID |
| Clerk API Call | ~100ms | Resolve name (if not in DB) |
| Token Signing | ~5ms | HMAC-SHA256 |
| Widget Auth | ~25ms | Verify signature |
Optimization: Name stored in MongoDB eliminates Clerk API call (~100ms savings)
๐ Configuration¶
| Setting | Value |
|---|---|
| Environment Variable | QUACKBACK_WIDGET_SECRET (required in all .env.* files) |
| Dashboard | https://together.visualli.ai |
๐ Implementation Files¶
Backend: /api/widget-sso/route.js
Frontend: index.html, global.d.ts, QuackbackIdentify.tsx, FeedbackButton.tsx, App.tsx