Implement cross-domain logout for Fireproof Cloud authentication
Problem
Users remain authenticated on connect.fireproof.direct after logging out locally, causing confusion and potential security concerns. The authentication persists across the domain boundary because:
- Clerk manages authentication on connect.fireproof.direct
- Local logout only clears Fireproof tokens, not the Clerk session
- No logout endpoint exists to clear the remote session
Proposed Solution
Implement a comprehensive cross-domain logout mechanism that clears both local and remote sessions.
Implementation Plan
1. Enhance TokenStrategie Interface
Add optional performLogout method to the TokenStrategie interface:
export interface TokenStrategie {
// ... existing methods
performLogout?(sthis: SuperThis, logger: Logger, opts: ToCloudOpts): Promise<boolean>;
}
2. Implement performLogout in Strategy Classes
RedirectStrategy - Use popup-based logout with enhanced error handling:
async performLogout(sthis: SuperThis, logger: Logger, opts: ToCloudOpts): Promise<boolean> {
this.stop();
if (this.overlayNode) {
this.overlayNode.style.display = 'none';
}
const webCtx = opts.context.get(WebCtx) as WebToCloudCtx;
const logoutUrl = webCtx.dashboardURI.replace('/fp/cloud/api/token', '/fp/cloud/api/logout');
return new Promise((resolve) => {
let popup: Window | null = null;
try {
popup = window.open(logoutUrl, 'FireproofLogout', 'width=400,height=300,popup=yes');
if (!popup) {
console.warn('Popup blocked - logout may be incomplete');
resolve(false);
return;
}
} catch (error) {
console.error('Failed to open logout popup:', error);
resolve(false);
return;
}
const handleMessage = (event: MessageEvent) => {
if (event.data?.type === 'fireproof-logout-complete') {
cleanup();
resolve(true);
}
};
// Handle manual popup closure
const checkClosed = setInterval(() => {
if (popup?.closed) {
cleanup();
resolve(false);
}
}, 1000);
const cleanup = () => {
window.removeEventListener('message', handleMessage);
clearInterval(checkClosed);
popup?.close();
};
window.addEventListener('message', handleMessage);
// Timeout fallback
setTimeout(() => {
cleanup();
resolve(false);
}, 30000);
});
}
IframeStrategy - Similar implementation using iframe communication SimpleTokenStrategy - Basic implementation returning true
3. Create performCompleteLogout Helper
Add to WebCtxImpl for coordinating the full logout process:
async performCompleteLogout(strategy?: TokenStrategie): Promise<void> {
// Call strategy's performLogout if available
if (strategy?.performLogout) {
await strategy.performLogout(this.sthis, logger, this.opts);
}
// Clear local tokens
await this.resetToken();
// Notify listeners
this.onAction();
}
4. Add Dashboard Logout Endpoint
Create /fp/cloud/api/logout endpoint that:
- Clears Clerk session cookies
- Posts completion message to parent window
- Returns appropriate CORS headers
5. Integration Points
Update existing disableSync() functionality to use the new logout mechanism when available.
Benefits
- Complete session cleanup across domains
- Consistent with existing popup-based auth flow
- Backward compatible (optional interface method)
- Proper error handling for popup blockers
- Clear user feedback on logout status
Questions to Address
- Should logout URL pattern (/token → /logout) be configurable?
- Should logout invalidate tokens for all apps or just current ledger?
- How to handle offline logout attempts?
- Token invalidation scope (ledger vs account level)?
Related Issues
- #1166 (Database name parameter in auth URLs)
- #1188 (merge() without save() persistence)
- #1208 (Original cross-domain logout issue)
Priority
High - This is a security and UX concern affecting all Fireproof Cloud users.
This looks perfect for our use case! We're using a custom ManualRedirectStrategy that extends RedirectStrategy, so we'll automatically inherit the performLogout() method once this is implemented.
Our current disableSync() function in vibes.diy calls tokenAndClaims.reset() but this leaves users authenticated on connect.fireproof.direct, causing confusion when they try to re-enable sync later.
The popup-based logout approach is consistent with our existing auth flow and the PostMessage communication pattern will work well with our architecture.
Looking forward to integrating this - it will solve a significant UX issue for our users! 🎉