Skip to content

Implement cross-domain logout for Fireproof Cloud authentication #1209

@jchris

Description

@jchris

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:

  1. Clerk manages authentication on connect.fireproof.direct
  2. Local logout only clears Fireproof tokens, not the Clerk session
  3. 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

  1. Should logout URL pattern (/token → /logout) be configurable?
  2. Should logout invalidate tokens for all apps or just current ledger?
  3. How to handle offline logout attempts?
  4. Token invalidation scope (ledger vs account level)?

Related Issues

Priority

High - This is a security and UX concern affecting all Fireproof Cloud users.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions