Skip to main content

Permission Caching Troubleshooting Guide

Problem Description

When a user's project access is revoked using RemoveUserFromProject, they may still be able to access the project through direct URLs for up to 1 hour. This happens due to Firebase JWT token caching.

Why This Happens

  1. Server-side permissions updated correctly - Firebase custom claims are immediately updated
  2. Client-side JWT tokens cached - User's browser has cached Firebase tokens with old custom claims
  3. Direct URL access bypasses frontend checks - When navigating to project URLs, the cached JWT still grants access

Technical Root Cause

Firebase JWT tokens contain custom claims and are cached by the Firebase JavaScript SDK for performance. When custom claims are updated on the server, existing client tokens don't immediately reflect the changes.

Caching Layers

  1. Firebase JavaScript SDK - Caches tokens for ~1 hour by default
  2. Browser - May cache API responses
  3. ESPv2 Proxy - May cache JWT validation results
  4. Server - Plan data may be cached

Solutions Implemented

1. Force Token Refresh for Permission-Sensitive Operations

File: web-ng-m3/src/app/services/auth.interceptor.ts

The AuthInterceptor now forces token refresh for critical operations:

// Use forceRefresh for permission-sensitive operations to ensure latest custom claims
const isPermissionSensitive = ['GetArchitecturalPlan', 'CheckProjectPermission', 'GetUserProjects'].includes(methodName);
const token = await user.getIdToken(isPermissionSensitive);

2. Firebase Token Refresh Service

File: web-ng-m3/src/app/services/firebase-auth.service.ts

Added method to force refresh Firebase tokens:

async refreshUserToken(): Promise<void> {
const user = await lastValueFrom(this.user$.pipe(take(1)));
if (user) {
// Force refresh the token to get updated custom claims
await user.getIdToken(true); // forceRefresh = true
}
}

3. Permission Change Notification Service

File: web-ng-m3/src/app/services/permission-refresh.service.ts

Coordinates token refresh and cache clearing when permissions change:

async notifyPermissionChange(type: 'invite' | 'remove', projectId: string, userEmail: string): Promise<void> {
// Force refresh the current user's token to get updated custom claims
await this.firebaseAuthService.refreshUserToken();

// Emit event for components to react (clear caches, refresh data, etc.)
this.permissionChangedSubject.next({ type, projectId, userEmail });
}

4. Automatic Cache Clearing

File: web-ng-m3/src/app/components/main-view/main-view.component.ts

Main view component listens for permission changes and clears caches:

this.permissionRefreshService.permissionChanged$
.pipe(takeUntil(this.destroy$))
.subscribe(({ type, projectId, userEmail }) => {
if (projectId === this.currentPlanId) {
this.planReviewerService.clearPlanDataCache();
this.complianceStateService.clearPlanStates(projectId);

// If user was removed, redirect to home
if (type === 'remove') {
this.router.navigate(['/']);
}
}
});

Manual Workarounds

If users still experience permission issues, they can:

1. Hard Browser Refresh

  • Press Ctrl+F5 (Windows/Linux) or Cmd+Shift+R (Mac)
  • This clears browser caches and forces fresh API calls

2. Clear Browser Storage

  • Open Developer Tools (F12)
  • Go to Application/Storage tab
  • Clear Local Storage and Session Storage for the site

3. Sign Out and Sign Back In

  • Click user menu → Sign Out
  • Sign back in to get fresh tokens

4. Incognito/Private Mode

  • Open site in incognito/private browsing mode
  • This bypasses all browser caches

Testing the Fix

Scenario 1: Remove User Access

  1. User has access to project A.0155.01-2025-06-12
  2. Owner calls RemoveUserFromProject RPC
  3. User's browser should immediately refresh tokens
  4. Direct URL access should now be denied

Scenario 2: Add User Access

  1. User lacks access to project PROJECT-123
  2. Owner calls InviteUserToProject RPC
  3. User's browser should refresh tokens
  4. User should immediately see project in project list

Monitoring and Debugging

Browser Console Logs

Look for these log messages:

[AuthInterceptor] Successfully got token for GetArchitecturalPlan (forceRefresh: true)
[FirebaseAuthService] Token refreshed successfully - updated permissions should now be active
[PermissionRefreshService] Permission change detected: remove user@example.com for project PROJECT-123
[MainView] Current project affected by permission change, clearing caches

Server Logs

Monitor gRPC server logs for permission checks:

User removed from project: project A.0155.01-2025-06-12, user contact@codetricks.org
Successfully updated Firebase custom claims for user contact@codetricks.org

Expected Timeline

ActionEffectTiming
RemoveUserFromProject calledServer updates Firebase custom claimsImmediate
Client token refreshFresh token with updated claims1-2 seconds
Permission-sensitive API callsUse refreshed tokenImmediate
Cache clearingRemoves stale dataImmediate
Access deniedUser sees proper error messageImmediate

Future Enhancements

  1. Real-time notifications - WebSocket/Server-Sent Events for instant permission updates
  2. Token revocation - Use Firebase Admin SDK to revoke specific tokens
  3. Permission verification endpoints - Dedicated API to check current user permissions
  4. Automatic retry logic - Retry failed requests with fresh tokens