feat: implement 72-hour JWT token refresh for daily notification plugin

- Add accessTokenForBackground() with 72-hour default expiration
  - Supports offline-first prefetch operations
  - Balances security with offline capability

- Implement proactive token refresh strategy
  - Refresh on component mount (DailyNotificationSection)
  - Refresh on app resume (Capacitor 'resume' event)
  - Refresh when notifications are enabled
  - Automatic refresh without user interaction

- Update CapacitorPlatformService.configureNativeFetcher()
  - Automatically retrieves activeDid from database
  - Generates 72-hour JWT tokens for background operations
  - Includes starred plans in configuration

- Add BroadcastReceivers to AndroidManifest.xml
  - DailyNotificationReceiver for scheduled notifications
  - BootReceiver for rescheduling after device reboot

- Add comprehensive documentation
  - JSDoc comments for all token-related functions
  - Inline comments explaining refresh strategy
  - Documentation section on authentication & token management

Benefits:
- No app wake-up required (refresh when app already open)
- Works offline (72-hour validity supports extended periods)
- Automatic (no user interaction required)
- Graceful degradation (uses cached content if refresh fails)
This commit is contained in:
Matthew Raymer
2025-11-06 12:44:06 +00:00
parent 5f17f6cb4e
commit 831532739c
10 changed files with 410 additions and 80 deletions

View File

@@ -241,7 +241,7 @@ export interface ScheduleOptions {
export interface NativeFetcherConfig {
apiServer: string;
jwt: string;
jwt: string; // Ignored - generated automatically by configureNativeFetcher
starredPlanHandleIds: string[];
}
```
@@ -250,6 +250,63 @@ export interface NativeFetcherConfig {
- **Capacitor**: Full implementation, all methods functional
- **Web/Electron**: Status/permission methods return `null`, scheduling methods throw errors with clear messages
### Authentication & Token Management
#### Background Prefetch Authentication
The daily notification plugin requires authentication tokens for background prefetch operations. The implementation uses a **hybrid token refresh strategy** that balances security with offline capability.
**Token Generation** (`src/libs/crypto/index.ts`):
- Function: `accessTokenForBackground(did, expirationMinutes?)`
- Default expiration: **72 hours** (4320 minutes)
- Token type: JWT with ES256K signing
- Payload: `{ exp, iat, iss: did }`
**Why 72 Hours?**
- Balances security (read-only prefetch operations) with offline capability
- Reduces need for app to wake itself for token refresh
- Allows plugin to work offline for extended periods (e.g., weekend trips)
- Longer than typical prefetch windows (5 minutes before notification)
**Token Refresh Strategy (Hybrid Approach)**:
1. **Proactive Refresh Triggers**:
- Component mount (`DailyNotificationSection.mounted()`)
- App resume (Capacitor `resume` event)
- Notification enabled (when user enables daily notifications)
2. **Refresh Implementation** (`DailyNotificationSection.refreshNativeFetcherConfig()`):
- Checks if notifications are supported and enabled
- Retrieves API server URL from settings
- Retrieves starred plans from settings
- Calls `configureNativeFetcher()` to generate fresh token
- Errors are logged but don't interrupt user experience
3. **Offline Behavior**:
- If token expires while offline → plugin uses cached content
- Next time app opens → token automatically refreshed
- No app wake-up required (refresh happens when app is already open)
**Configuration Flow** (`CapacitorPlatformService.configureNativeFetcher()`):
1. Retrieves active DID from `active_identity` table (single source of truth)
2. Generates JWT token with 72-hour expiration using `accessTokenForBackground()`
3. Configures plugin with API server URL, active DID, and JWT token
4. Plugin stores token in its Room database for background workers
**Security Considerations**:
- Tokens are used only for read-only prefetch operations
- Tokens are stored securely in plugin's Room database
- Tokens are refreshed proactively to minimize exposure window
- No private keys are exposed to native code
- Token generation happens in TypeScript (no Java crypto compatibility issues)
**Error Handling**:
- Returns `null` if active DID not found (no user logged in)
- Returns `null` if JWT generation fails
- Logs errors but doesn't throw (allows graceful degradation)
- Refresh failures don't interrupt user experience (plugin uses cached content)
### Component Architecture
#### Views Structure