critical: Confirm plugin must know when activeDid changes

- Added critical requirement that plugin MUST be notified of activeDid changes
- Enhanced plugin interface with onActiveDidChange() callback method
- Added clearCacheForNewIdentity() and refreshAuthenticationForNewIdentity() methods
- Updated integration examples to include activeDid change listeners
- Created comprehensive ActiveDid change requirements document covering:
  * Security implications of not detecting changes
  * Event-based notification pattern implementation
  * Cache clearing and authentication refresh requirements
  * Testing scenarios for identity switching
  * Platform-specific considerations and edge cases
  * Performance optimization for rapid identity changes

This addresses the critical data integrity and security requirement that the plugin
must know when TimeSafari users switch identities to prevent data leakage.
This commit is contained in:
Matthew Raymer
2025-10-02 09:05:08 +00:00
parent 51034998dd
commit 1939b560d9
2 changed files with 223 additions and 3 deletions

View File

@@ -0,0 +1,204 @@
# ActiveDid Change Requirements
**Author**: Matthew Raymer
**Version**: 1.0.0
**Created**: 2025-10-02 12:00:00 UTC
**Last Updated**: 2025-10-02 12:00:00 UTC
## Critical Requirement: Plugin Must Know When activeDid Changes
**CONFIRMED**: The plugin **must** be notified when activeDid changes to maintain data integrity and security.
## Why This Is Critical
### **Security Implications**
- **Authentication Isolation**: Each activeDid has different authentication tokens
- **Data Privacy**: Cached content belongs to specific user identity
- **Authorization**: API calls must use correct activeDid for proper authorization
### **Data Integrity Issues**
- **Cache Pollution**: Wrong user's data could leak between identities
- **Stale Authentication**: Old JWT tokens become invalid for new identity
- **Background Tasks**: Tasks running with wrong identity context
### **User Experience Problems**
```typescript
// PROBLEMATIC SCENARIO WITHOUT CHANGE NOTIFICATION:
// 1. User A schedules notification for "my offers"
// 2. User switches to User B
// 3. Plugin doesn't know about change
// 4. Plugin delivers User A's personal offer data to User B ❌
```
## Solution: Event-Based Change Notification
### **TimeSafari Host Application**
```typescript
// In PlatformServiceMixin.ts - CRITICAL PATTERN
async $updateActiveDid(newDid: string | null): Promise<void> {
// ... existing activeDid update logic ...
// MUST notify Daily Notification Plugin
await this.$notifyDailyNotificationService(newDid);
}
async $notifyDailyNotificationService(newActiveDid: string): Promise<void> {
const event = new CustomEvent('activeDidChanged', {
detail: { activeDid: newActiveDid }
});
document.dispatchEvent(event);
}
```
### **Plugin Side Implementation**
```typescript
// In Daily Notification Plugin - MANDATORY LISTENING
export class EnhancedDailyNotificationPlugin {
private currentActiveDid: string | null = null;
constructor() {
this.setupActiveDidChangeListener();
}
private setupActiveDidChangeListener(): void {
document.addEventListener('activeDidChanged', (event: CustomEvent) => {
this.handleActiveDidChange(event.detail.activeDid);
});
}
private async handleActiveDidChange(newActiveDid: string): Promise<void> {
if (newActiveDid !== this.currentActiveDid) {
logger.info(`[Plugin] ActiveDid changing from ${this.currentActiveDid} to ${newActiveDid}`);
// CRITICAL ACTIONS REQUIRED:
await this.clearCacheForNewIdentity();
await this.refreshAuthenticationForNewIdentity(newActiveDid);
await this.updateBackgroundTaskIdentity(newActiveDid);
this.currentActiveDid = newActiveDid;
}
}
async clearCacheForNewIdentity(): Promise<void> {
// Clear all cached content to prevent data leakage
await this.clearStoredContent();
await this.clearAuthenticationTokens();
}
async refreshAuthenticationForNewIdentity(activeDid: string): Promise<void> {
// Generate new JWT tokens with correct activeDid
const newJwt = await this.generateJWT(activeDid);
this.currentAuthToken = newJwt;
}
async updateBackgroundTaskIdentity(activeDid: string): Promise<void> {
// Update background tasks to use new identity
await this.pauseBackgroundTasks();
await this.configureBackgroundTasksForIdentity(activeDid);
await this.resumeBackgroundTasks();
}
}
```
## Implementation Requirements
### **Host Application Responsibilities**
1. **Event Dispatch**: Must dispatch `activeDidChanged` events
2. **Timing**: Dispatch immediately after updating active_identity table
3. **Reliability**: Event must reach all plugin listeners
### **Plugin Responsibilities**
1. **Event Listening**: Must listen for `activeDidChanged` events
2. **Cache Clearing**: Must clear all cached content for new identity
3. **Authentication Refresh**: Must generate new tokens with updated activeDid
4. **Background Task Update**: Must restart background tasks with new context
5. **Error Handling**: Must handle failures gracefully during identity transition
## Testing Requirements
### **Integration Tests**
```typescript
describe('ActiveDid Change Handling', () => {
it('should clear cache when activeDid changes', async () => {
// Set up initial identity
await plugin.setActiveDidFromHost('did:alice:123');
await plugin.cacheContentData({ /* Alice's data */ });
// Simulate identity change
const event = new CustomEvent('activeDidChanged', {
detail: { activeDid: 'did:bob:456' }
});
document.dispatchEvent(event);
// Verify cache is cleared
expect(await plugin.getCachedContent()).toBeNull();
});
it('should refresh authentication tokens', async () => {
// Set initial identity
const aliceToken = await plugin.generateJWT('did:alice:123');
// Change identity
document.dispatchEvent(new CustomEvent('activeDidChanged', {
detail: { activeDid: 'did:bob:456' }
}));
// Verify new token with correct identity
const bobToken = await plugin.getCurrentAuthToken();
expect(bobToken.payload.sub).toBe('did:bob:456');
});
it('should handle multiple rapid identity changes', async () => {
// Test rapid switching between identities
const identities = ['did:alice:123', 'did:bob:456', 'did:alice:123'];
for (const identity of identities) {
document.dispatchEvent(new CustomEvent('activeDidChanged', {
detail: { activeDid: identity }
}));
await new Promise(resolve => setTimeout(resolve, 100)); // Brief pause
}
// Ensure plugin ends up in correct state
expect(plugin.currentActiveDid).toBe('did:alice:123');
});
});
```
### **Edge Case Testing**
- **Network Failure**: Identity changes during network failures
- **Background Mode**: Identity changes when app is backgrounded
- **Rapid Switches**: Multiple identity changes in quick succession
- **Invalid activeDid**: Change to empty or invalid activeDid
## Platform-Specific Considerations
### **Android/Electron**
- Plugin can detect activeDid changes in background via database polling
- Backup mechanism if event-based notification fails
### **Web**
- Event-based notification is primary mechanism
- Fallback to periodic activeDid checking
### **iOS**
- Background tasks can check activeDid changes
- Event-based notification for foreground changes
## Performance Considerations
### **Change Detection Optimization**
- Debounce rapid identity changes
- Batch cache clearing operations
- Minimize background task restarts
### **Resource Cleanup**
- Clear authentication tokens immediately
- Clean up cached content efficiently
- Avoid memory leaks during transitions
---
**Status**: Critical requirement documented - Implementation required
**Next Steps**: Add activeDid change listeners to plugin implementation
**Security Impact**: HIGH - Prevents data leakage between user identities

View File

@@ -271,11 +271,15 @@ await plugin.configureDatabase({
const activeIdentity = await this.$getActiveIdentity(); const activeIdentity = await this.$getActiveIdentity();
await plugin.setActiveDidFromHost(activeIdentity.activeDid); await plugin.setActiveDidFromHost(activeIdentity.activeDid);
// CRITICAL: Set up activeDid change listener
plugin.onActiveDidChange(async (newActiveDid) => {
await plugin.clearCacheForNewIdentity();
await plugin.refreshAuthenticationForNewIdentity(newActiveDid);
logger.info(`[TimeSafari] ActiveDid changed to: ${newActiveDid}`);
});
// Enable automatic background lookup // Enable automatic background lookup
await plugin.enableAutoActiveDidMode(); await plugin.enableAutoActiveDidMode();
// Plugin can fetch content and cache independently
const results = await plugin.executeContentFetch(config);
``` ```
**Web: Host-Managed Database with Hybrid activeDid** **Web: Host-Managed Database with Hybrid activeDid**
@@ -345,6 +349,11 @@ interface EnhancedDailyNotificationPlugin {
// TimeSafari Integration Methods // TimeSafari Integration Methods
initializeFromTimeSafari(): Promise<void>; initializeFromTimeSafari(): Promise<void>;
listenForActiveDidChanges(): Promise<void>; listenForActiveDidChanges(): Promise<void>;
// Critical: ActiveDid Change Handling
onActiveDidChange(callback: (newActiveDid: string) => Promise<void>): void;
clearCacheForNewIdentity(): Promise<void>;
refreshAuthenticationForNewIdentity(activeDid: string): Promise<void>;
} }
``` ```
@@ -356,6 +365,11 @@ interface EnhancedDailyNotificationPlugin {
- **Implement** dual-mode activeDid access: - **Implement** dual-mode activeDid access:
- **Foreground**: Host provides activeDid via `setActiveDidFromHost()` - **Foreground**: Host provides activeDid via `setActiveDidFromHost()`
- **Background**: Plugin looks up activeDid via `refreshActiveDidFromDatabase()` - **Background**: Plugin looks up activeDid via `refreshActiveDidFromDatabase()`
- **Critical**: Plugin **MUST** know when activeDid changes for:
- **Event-Based Notification**: Listen for `activeDidChanged` events from TimeSafari
- **Cache Invalidation**: Clear cached content when user switches identity
- **Token Refresh**: Generate new JWT tokens with updated activeDid
- **Background Task Coordination**: Stop/restart tasks with new identity context
- **Coordinate** with TimeSafari's PlatformServiceMixin for identity changes - **Coordinate** with TimeSafari's PlatformServiceMixin for identity changes
## Migration & Testing Strategy ## Migration & Testing Strategy
@@ -375,6 +389,8 @@ interface EnhancedDailyNotificationPlugin {
- Test `setActiveDidFromHost()` with TimeSafari PlatformServiceMixin - Test `setActiveDidFromHost()` with TimeSafari PlatformServiceMixin
- Test `refreshActiveDidFromDatabase()` with background tasks - Test `refreshActiveDidFromDatabase()` with background tasks
- Test `enableAutoActiveDidMode()` for automatic synchronization - Test `enableAutoActiveDidMode()` for automatic synchronization
- **Critical**: Test `onActiveDidChange()` listener with identity switches
- Test cache invalidation and token refresh during activeDid changes
- **Background testing** on real devices (doze mode, app backgrounding) - **Background testing** on real devices (doze mode, app backgrounding)
- **Authentication testing** with actual DID credentials from TimeSafari active_identity table - **Authentication testing** with actual DID credentials from TimeSafari active_identity table
- **Cross-platform testing** for Android/Electron (SQLite access) vs Web (host delegation) patterns - **Cross-platform testing** for Android/Electron (SQLite access) vs Web (host delegation) patterns