fix(test-app): remove aud claim from JWT to resolve server validation error

Remove the aud (audience) claim from JWT payloads. The server's did-jwt
verification requires an audience option when aud is present, but the server
isn't configured to validate it, causing "JWT audience is required but your
app address has not been configured" errors.

Changes:
- Removed aud claim from JWT payload in generateEndorserJWT()
- Updated key derivation to User Zero's specific path (m/84737769'/0'/0'/0')
- Added public key verification against expected User Zero key
- Enhanced JWT diagnostics logging throughout
- Added alarm deduplication optimization (prevent duplicate alarms for same time)

Verified: JWT validation now passes (token length 360→333 chars, no audience
error). New error is API parameter validation (afterId required - separate issue).
This commit is contained in:
Matthew Raymer
2025-11-02 09:46:54 +00:00
parent 5272cc0912
commit a421bb5d41
5 changed files with 304 additions and 37 deletions

View File

@@ -36,6 +36,10 @@ public class DailyNotificationScheduler {
private final AlarmManager alarmManager;
private final ConcurrentHashMap<String, PendingIntent> scheduledAlarms;
// Track scheduled times to prevent duplicate alarms for same time
// Maps scheduledTime (ms) -> notificationId that has alarm scheduled
private final ConcurrentHashMap<Long, String> scheduledTimeToId;
// PendingIntent management
private PendingIntentManager pendingIntentManager;
@@ -55,6 +59,7 @@ public class DailyNotificationScheduler {
this.context = context;
this.alarmManager = alarmManager;
this.scheduledAlarms = new ConcurrentHashMap<>();
this.scheduledTimeToId = new ConcurrentHashMap<>();
this.pendingIntentManager = new PendingIntentManager(context);
Log.d(TAG, "DailyNotificationScheduler initialized with PendingIntentManager");
@@ -120,6 +125,36 @@ public class DailyNotificationScheduler {
Log.d(TAG, "Phase 3: Cancelling existing alarm for notification: " + content.getId());
cancelNotification(content.getId());
// Get scheduled time and check for duplicate alarms at same time
long triggerTime = content.getScheduledTime();
long toleranceMs = 60 * 1000; // 1 minute tolerance for DST/clock adjustments
// Cancel any existing alarm for the same scheduled time (within tolerance)
// This prevents multiple notifications scheduled for same time from creating duplicate alarms
// Check all scheduled times to find any within tolerance
java.util.List<String> duplicateIds = new java.util.ArrayList<>();
for (java.util.Map.Entry<Long, String> entry : scheduledTimeToId.entrySet()) {
Long scheduledTime = entry.getKey();
String existingId = entry.getValue();
// Skip if it's the same notification ID or time difference is too large
if (existingId.equals(content.getId()) ||
Math.abs(scheduledTime - triggerTime) > toleranceMs) {
continue;
}
// Found an alarm scheduled for a time very close to this one
duplicateIds.add(existingId);
}
// Cancel any duplicate alarms found
for (String duplicateId : duplicateIds) {
Log.w(TAG, "Phase 3: Cancelling duplicate alarm for time " +
formatTime(triggerTime) + " (existing ID: " + duplicateId +
", new ID: " + content.getId() + ")");
cancelNotification(duplicateId);
}
// Create intent for the notification
Intent intent = new Intent(context, DailyNotificationReceiver.class);
intent.setAction(ACTION_NOTIFICATION);
@@ -143,8 +178,10 @@ public class DailyNotificationScheduler {
// Store the pending intent
scheduledAlarms.put(content.getId(), pendingIntent);
// Track scheduled time to notification ID mapping
scheduledTimeToId.put(triggerTime, content.getId());
// Schedule the alarm
long triggerTime = content.getScheduledTime();
boolean scheduled = scheduleAlarm(pendingIntent, triggerTime);
if (scheduled) {
@@ -421,6 +458,17 @@ public class DailyNotificationScheduler {
pendingIntentManager.cancelAlarm(pendingIntent);
pendingIntent.cancel();
Log.d(TAG, "Cancelled existing alarm for notification: " + notificationId);
// Remove from time-to-ID mapping by finding and removing the entry
java.util.List<Long> timesToRemove = new java.util.ArrayList<>();
for (java.util.Map.Entry<Long, String> entry : scheduledTimeToId.entrySet()) {
if (entry.getValue().equals(notificationId)) {
timesToRemove.add(entry.getKey());
}
}
for (Long time : timesToRemove) {
scheduledTimeToId.remove(time);
}
} else {
Log.d(TAG, "No existing alarm found to cancel for notification: " + notificationId);
}
@@ -441,6 +489,7 @@ public class DailyNotificationScheduler {
}
scheduledAlarms.clear();
scheduledTimeToId.clear();
Log.i(TAG, "All notifications cancelled");
} catch (Exception e) {