docs(notifications): document debug panel auth settings and bypassAuth

Add doc/notification-debug-panel.md as the canonical panel reference.
Update ngrok guides, README, and analysis doc to describe independent
Backend URL, Test Mode, and Skip JWT Authentication settings, including
recommended configs for hosted test servers vs local ngrok.
This commit is contained in:
Jose Olarte III
2026-07-07 19:21:18 +08:00
parent 82380b3d35
commit f23fe65078
5 changed files with 265 additions and 17 deletions

View File

@@ -112,7 +112,24 @@ See [Logging Configuration Guide](doc/logging-configuration.md) for complete det
## Notification Debug Panel (dev builds)
In non-production bundles (for example `vite dev` or a Vite build whose mode is not `production`), the **Notification Debug Panel** at `/dev/notifications` helps you inspect pending notifications, trigger mock refreshes and wakeup pings, and review notification-related debug logs when working on local scheduling (including Capacitor). From the UI, open **Account**, enable **Show All General Advanced Functions**, then use the **Notification Debug Panel** link.
In non-production bundles (for example `vite dev` or a Vite build whose mode is not `production`), the **Notification Debug Panel** at `/dev/notifications` helps you test notification registration, backend refresh, WAKEUP_PING handling, and local schedule inspection on native builds.
**Access:** **Account** → enable **Show All General Advanced Functions****Notification Debug Panel**.
Key configuration (independent settings):
- **Notification Backend URL** — which notification server receives API calls
- **Test Mode** — `testMode` sent in JSON request bodies (default on)
- **Skip JWT Authentication (Local Development Only)** — omit JWT headers for local unauthenticated backends (default off)
See [doc/notification-debug-panel.md](doc/notification-debug-panel.md) for controls, recommended settings (hosted test server vs local ngrok), and troubleshooting.
Platform-specific end-to-end guides:
- [doc/local-android-testing-ngrok.md](doc/local-android-testing-ngrok.md)
- [doc/local-ios-testing-ngrok.md](doc/local-ios-testing-ngrok.md)
## Database Clearing (development)
### Quick Usage
```bash

View File

@@ -80,15 +80,16 @@ These blocks can be carried into `doc/local-android-testing-ngrok.md` (proposed
- Non-production build required for Notification Debug Panel
- Account → **Show All General Advanced Functions**`/dev/notifications`
- **Notification Backend URL**, **Save Backend URL**
- `localStorage`: `notificationDebug.backendBaseUrl`, `notificationDebug.testMode`
- Optional programmatic override via `@/services/notifications` (`setBackendBaseUrl`, `setTestMode`, `getNotificationApiBaseUrl`)
- `localStorage`: `notificationDebug.backendBaseUrl`, `notificationDebug.testMode`, `notificationDebug.bypassAuth`
- Optional programmatic override via `@/services/notifications` (`setBackendBaseUrl`, `setTestMode`, `setBypassAuth`, `getNotificationApiBaseUrl`)
### Debug panel usage (iOS §6 table, §8 “Two Simulate WAKEUP_PING buttons”)
| Control | Android relevance |
|---------|-------------------|
| Notification Backend URL | Same |
| Test Mode | Same (`testMode: true` on API) |
| Test Mode | Same (`testMode` in JSON body) |
| Skip JWT Authentication | Same — explicit opt-in for unauthenticated local backends (default off) |
| Register Token Now | Same (`POST /notifications/register`) |
| Refresh Notifications | Same |
| Simulate WAKEUP_PING (backend) | Same — isolates ngrok + refresh without FCM |

View File

@@ -277,6 +277,8 @@ Before ngrok end-to-end testing, confirm:
The app normally calls `APP_SERVER` (from `VITE_APP_SERVER`). For local wakeup testing, override the notification API base URL without rebuilding.
For a full panel reference (configuration, authentication, and troubleshooting), see [notification-debug-panel.md](./notification-debug-panel.md).
### Open the panel
1. Use a **non-production** build (e.g. `build:android:dev` or `build:android:test`).
@@ -287,15 +289,28 @@ The app normally calls `APP_SERVER` (from `VITE_APP_SERVER`). For local wakeup t
| Control | Purpose |
|---------|---------|
| **Notification Backend URL** | Paste ngrok HTTPS URL → **Save Backend URL** |
| **Test Mode** | Sends `testMode: true` on register/refresh (default on when unset in storage) |
| **Notification Backend URL** | Paste ngrok HTTPS URL → **Save Backend URL** (changes target server only) |
| **Test Mode** | Sends `testMode: true/false` in register/refresh JSON bodies (default on when unset in storage) |
| **Skip JWT Authentication (Local Development Only)** | When on, omits `Authorization` headers for local servers that accept unauthenticated requests (default **off**) |
| **Register Token Now** | `POST /notifications/register` with current FCM token and `platform: "android"` |
| **Refresh Notifications** | `POST /notifications/refresh` (same as post-wakeup flow) |
| **Simulate WAKEUP_PING (Local)** | Calls refresh API directly (no FCM) — quick backend + ngrok test |
| **Send Real WAKEUP_PING** | `POST /debug/send-wakeup` on the backend override URL; server sends a real FCM `WAKEUP_PING` to the panels current FCM token (see below) |
| **Event Log** | Shared `[Notifications]` panel log (100 entries) |
Persistence: `localStorage` keys `notificationDebug.backendBaseUrl` and `notificationDebug.testMode` (`NotificationDebugConfig.ts`).
Persistence: `localStorage` keys `notificationDebug.backendBaseUrl`, `notificationDebug.testMode`, and `notificationDebug.bypassAuth` (`NotificationDebugConfig.ts`).
### Authentication vs backend URL
These settings are **independent**:
- **Backend URL** — which server receives `/notifications/*` and `/debug/*` calls.
- **Test Mode** — `testMode` field in JSON request bodies only.
- **Skip JWT Authentication** — whether `Authorization: Bearer …` is sent (via `getNotificationApiHeaders()`).
For a **hosted shared test server** (for example `https://test-notify-api.timesafari.app`): set the backend URL, keep **Test Mode** on if the server requires it, and leave **Skip JWT Authentication** **off**. Ensure an active DID and endorser session exist in the app.
For **local ngrok / localhost**: set the backend URL to your tunnel or `http://127.0.0.1:PORT`. Enable **Skip JWT Authentication** only if your local **notification-wakeup-service** intentionally accepts unauthenticated requests.
### testMode
@@ -347,7 +362,7 @@ On a **successful end-to-end** run (HTTP success from the panel, then FCM delive
[Notifications] Refresh completed (WAKEUP_PING) in …ms (scheduled N)
```
Intermediate lines (e.g. `WAKEUP_PING received — will trigger refresh`, `Schedule replacement: …`, auth bypass messages) are normal. ngrok should also show a new `POST /notifications/refresh` after the push is handled.
Intermediate lines (e.g. `WAKEUP_PING received — will trigger refresh`, `Schedule replacement: …`, `Using authenticated notification request` or auth bypass messages when **Skip JWT Authentication** is on) are normal. ngrok should also show a new `POST /notifications/refresh` after the push is handled.
#### Send Real WAKEUP_PING vs end-to-end success
@@ -363,11 +378,13 @@ From Chrome DevTools attached to the WebView (`chrome://inspect` → your app):
import {
setBackendBaseUrl,
setTestMode,
setBypassAuth,
getNotificationApiBaseUrl,
} from "@/services/notifications";
setBackendBaseUrl("https://abc123.ngrok-free.app");
setTestMode(true);
setBypassAuth(true); // local dev only — omit for hosted servers that require JWT
getNotificationApiBaseUrl(); // → ngrok URL
```
@@ -509,7 +526,7 @@ If wakeup works on a **Pixel** but fails on an OEM phone, assume battery policy
2. Start **ngrok** and copy the HTTPS URL.
3. Install a **non-production** Android build with `google-services.json` in place.
4. Grant notification permission when prompted (or enable in Settings).
5. Open **Notification Debug Panel** → paste ngrok URL → **Save Backend URL**; confirm **Test Mode** is on.
5. Open **Notification Debug Panel** → paste ngrok URL → **Save Backend URL**; confirm **Test Mode** is on; enable **Skip JWT Authentication** only if your local backend accepts unauthenticated requests.
6. Tap **Register Token Now** → confirm ngrok `POST /notifications/register` and `[Notifications] Token registration success` in Event Log / logcat.
7. Tap **Refresh Notifications** → confirm `POST /notifications/refresh` and `Refresh completed in Nms (scheduled X)` in Event Log.
8. Optional: tap **Simulate WAKEUP_PING (Local)** to verify ngrok + refresh without FCM.
@@ -837,7 +854,7 @@ Structured checks for local ngrok + FCM testing. Each item lists **symptoms**, *
**Symptoms:** Event Log shows token registration failure; no FCM token in debug panel; ngrok has no `POST /notifications/register`; curl register fails.
**Likely causes:** Notification permission denied; missing `google-services.json`; Firebase package mismatch (`app.timesafari.app`); `VITE_FIREBASE_*` not in build; auth headers missing for register; duplicate-token skip.
**Likely causes:** Notification permission denied; missing `google-services.json`; Firebase package mismatch (`app.timesafari.app`); `VITE_FIREBASE_*` not in build; JWT auth failure or **Skip JWT Authentication** mismatch with server expectations; duplicate-token skip.
**Verification:**
@@ -846,7 +863,7 @@ Structured checks for local ngrok + FCM testing. Each item lists **symptoms**, *
3. Panel shows a token string before **Register Token Now**.
4. ngrok inspect UI (`http://127.0.0.1:4040`) for register request status body.
**Fixes:** Grant permission and cold-start app; add `android/app/google-services.json` and rebuild; align Firebase Android app ID with Gradle `applicationId`; rebuild with correct `.env`; tap **Register Token Now** (forces re-register); fix DID/auth if register returns 401.
**Fixes:** Grant permission and cold-start app; add `android/app/google-services.json` and rebuild; align Firebase Android app ID with Gradle `applicationId`; rebuild with correct `.env`; tap **Register Token Now** (forces re-register); for JWT-required servers, ensure active DID/endorser session and **Skip JWT Authentication** off; for local unauthenticated backends, enable **Skip JWT Authentication** (see [notification-debug-panel.md](./notification-debug-panel.md)).
---
@@ -985,7 +1002,7 @@ Compare with panel **Backend Status** and Event Log error text.
| File | Purpose |
|------|---------|
| `src/services/notifications/NotificationDebugConfig.ts` | Backend URL + testMode override |
| `src/services/notifications/NotificationDebugConfig.ts` | Backend URL, testMode, and bypassAuth overrides |
| `src/services/notifications/NotificationDebugEvents.ts` | Panel event log + `logNotification()` |
| `src/services/notifications/notificationLog.ts` | Structured log helpers |
| `src/services/notifications/NotificationService.ts` | `POST /notifications/register` |
@@ -1007,6 +1024,7 @@ Compare with panel **Backend Status** and Event Log error text.
- [notification-from-api-call.md](./notification-from-api-call.md)
- [notification-permissions-and-rollovers.md](./notification-permissions-and-rollovers.md)
- [BUILDING.md](../BUILDING.md) — Android build commands
- [notification-debug-panel.md](./notification-debug-panel.md) — panel controls, authentication, troubleshooting
- [Notification Debug Panel (README)](../README.md#notification-debug-panel-dev-builds)
For plugin-native behavior (exact alarms, Android pending inspector), see **daily-notification-plugin** documentation. For FCM payload format and `/debug/send-wakeup` contract, see **notification-wakeup-service**.

View File

@@ -283,6 +283,8 @@ Before ngrok end-to-end testing, confirm:
The app normally calls `APP_SERVER` (from `VITE_APP_SERVER`). For local wakeup testing, override the notification API base URL without rebuilding.
For a full panel reference (configuration, authentication, and troubleshooting), see [notification-debug-panel.md](./notification-debug-panel.md).
### Open the panel
1. Use a **non-production** bundle (e.g. dev/test build).
@@ -293,14 +295,28 @@ The app normally calls `APP_SERVER` (from `VITE_APP_SERVER`). For local wakeup t
| Control | Purpose |
|---------|---------|
| **Notification Backend URL** | Paste ngrok HTTPS URL → **Save Backend URL** |
| **Test Mode** | Sends `testMode: true` on register/refresh (default on when unset in storage) |
| **Notification Backend URL** | Paste ngrok HTTPS URL → **Save Backend URL** (changes target server only) |
| **Test Mode** | Sends `testMode: true/false` in register/refresh JSON bodies (default on when unset in storage) |
| **Skip JWT Authentication (Local Development Only)** | When on, omits `Authorization` headers for local servers that accept unauthenticated requests (default **off**) |
| **Register Token Now** | `POST /notifications/register` with current FCM token |
| **Refresh Notifications** | `POST /notifications/refresh` (same as post-wakeup flow) |
| **Simulate WAKEUP_PING** | Calls refresh API directly (no FCM) — quick backend test |
| **Simulate WAKEUP_PING (Local)** | Calls refresh API directly (no FCM) — quick backend test |
| **Send Real WAKEUP_PING** | `POST /debug/send-wakeup`; server sends real FCM `WAKEUP_PING` (Android doc has full flow) |
| **Event Log** | Shared `[Notifications]` panel log (100 entries) |
Persistence: `localStorage` keys `notificationDebug.backendBaseUrl` and `notificationDebug.testMode` (`NotificationDebugConfig.ts`).
Persistence: `localStorage` keys `notificationDebug.backendBaseUrl`, `notificationDebug.testMode`, and `notificationDebug.bypassAuth` (`NotificationDebugConfig.ts`).
### Authentication vs backend URL
These settings are **independent**:
- **Backend URL** — which server receives notification API calls.
- **Test Mode** — `testMode` field in JSON request bodies only.
- **Skip JWT Authentication** — whether JWT `Authorization` headers are sent.
For a **hosted shared test server**: set the backend URL, keep **Test Mode** on if required, leave **Skip JWT Authentication** **off**, and ensure an active DID exists.
For **local ngrok**: set the backend URL; enable **Skip JWT Authentication** only if your local backend accepts unauthenticated requests.
### Programmatic override (optional)
@@ -310,11 +326,13 @@ From Safari Web Inspector or a dev console attached to the WebView:
import {
setBackendBaseUrl,
setTestMode,
setBypassAuth,
getNotificationApiBaseUrl,
} from "@/services/notifications";
setBackendBaseUrl("https://abc123.ngrok-free.app");
setTestMode(true);
setBypassAuth(true); // local dev only — omit for hosted servers that require JWT
getNotificationApiBaseUrl(); // → ngrok URL
```
@@ -502,7 +520,7 @@ Confirm parameters (token vs deviceId, auth headers) in that repos README or
| File | Purpose |
|------|---------|
| `src/services/notifications/NotificationDebugConfig.ts` | Backend URL + testMode override |
| `src/services/notifications/NotificationDebugConfig.ts` | Backend URL, testMode, and bypassAuth overrides |
| `src/services/notifications/NotificationDebugEvents.ts` | Panel event log + `logNotification()` |
| `src/services/notifications/notificationLog.ts` | Structured log helpers |
| `src/services/notifications/NotificationService.ts` | `POST /notifications/register` |
@@ -515,6 +533,7 @@ Confirm parameters (token vs deviceId, auth headers) in that repos README or
## 13. Related docs
- [notification-debug-panel.md](./notification-debug-panel.md) — panel controls, authentication, troubleshooting
- [Notification Debug Panel (README)](../README.md#notification-debug-panel-dev-builds)
- [notification-system-overview.md](./notification-system-overview.md)
- [notification-from-api-call.md](./notification-from-api-call.md)

View File

@@ -0,0 +1,193 @@
# Notification Debug Panel
**Created:** 2026-07-07
**Audience:** Developers testing notification registration, refresh, and WAKEUP_PING flows on native (iOS/Android) dev builds.
The **Notification Debug Panel** is a dev-only UI for exercising the same notification orchestration paths the production app uses: FCM token registration, backend refresh, wakeup handling, and local schedule inspection. It does not duplicate scheduling logic.
---
## Access
1. Use a **non-production** build (for example `build:android:dev`, `build:ios:dev`, or `vite dev` with a non-`production` mode).
2. Open **Account** → enable **Show All General Advanced Functions**.
3. Open **Notification Debug Panel** (route `/dev/notifications`).
On native platforms, grant notification permission when prompted so FCM token registration and the debug actions work.
---
## Configuration (Backend Testing)
Settings persist in `localStorage` via `NotificationDebugConfig.ts`:
| Key | Default | Purpose |
|-----|---------|---------|
| `notificationDebug.backendBaseUrl` | *(unset — use `APP_SERVER`)* | Override which notification server receives API calls |
| `notificationDebug.testMode` | `true` | Sent in JSON request bodies (`testMode: true/false`) |
| `notificationDebug.bypassAuth` | `false` | When `true`, omit JWT `Authorization` headers on notification API calls |
All notification API requests (`/notifications/register`, `/notifications/refresh`, `/debug/send-wakeup`, etc.) obtain headers through `getNotificationApiHeaders()` in `notificationApiAuth.ts`.
### Notification Backend URL
Paste a base URL (no trailing slash) and tap **Save Backend URL**. This changes **only** which server the app calls (`getNotificationApiBaseUrl()`). It does **not** disable JWT authentication.
Leave empty to use the built-in default (`APP_SERVER` from `VITE_APP_SERVER`).
### Test Mode
When enabled (default if never saved), register and refresh requests include `"testMode": true` in the JSON body. The backend can use this to return dev-friendly schedules or route test traffic separately from production.
Test Mode is **independent of authentication**. It does not control whether `Authorization` headers are sent.
### Skip JWT Authentication (Local Development Only)
When **off** (default), the app resolves the active DID and sends `Authorization: Bearer …` on notification API calls.
When **on**, requests include only `Content-Type: application/json` — for local servers (localhost or ngrok) that intentionally accept unauthenticated notification requests during development.
Enable this **only** for local development backends that do not require JWT. Hosted shared test servers that require normal app authentication should leave this **off**.
The panel **Backend Status** section shows the active URL, `testMode`, and `bypassAuth` values.
---
## Recommended settings
### Hosted test server
Example: `https://test-notify-api.timesafari.app`
| Setting | Value |
|---------|-------|
| **Notification Backend URL** | `https://test-notify-api.timesafari.app` |
| **Test Mode** | **ON** (if the server expects `testMode: true`) |
| **Skip JWT Authentication** | **OFF** |
Ensure the app has an **active identity (DID)** with a valid endorser session so JWT headers can be built.
### Local localhost / ngrok development
Example: `https://abc123.ngrok-free.app` or `http://127.0.0.1:3000`
| Setting | Value |
|---------|-------|
| **Notification Backend URL** | Your local or ngrok URL |
| **Test Mode** | **ON** or **OFF** — match what your local **notification-wakeup-service** expects |
| **Skip JWT Authentication** | **ON** only if the local server accepts unauthenticated requests; **OFF** if it validates JWT like production |
---
## Backend Testing actions
| Action | What it does |
|--------|----------------|
| **Register Token Now** | `POST {backend}/notifications/register` with current FCM token, `deviceId`, `platform`, and `testMode`. Forces re-registration (bypasses duplicate-token skip). |
| **Refresh Notifications** | `POST {backend}/notifications/refresh` — same path used after a real WAKEUP_PING. Applies returned schedule to the native plugin. |
| **Simulate WAKEUP_PING (Local)** | Calls the refresh API directly (no FCM). Quick test of backend URL + auth + refresh parsing without push delivery. |
| **Send Real WAKEUP_PING** | `POST {backend}/debug/send-wakeup`; server sends a real FCM data message with `data.type = "WAKEUP_PING"`. Exercises backend → FCM → Capacitor listener → refresh → reschedule. Background the app before expecting delivery. |
**Current FCM Token** displays the last token from Capacitor/Firebase registration. **Event Log** shows the last 100 `[Notifications]` messages (also visible in logcat / Xcode console on native).
---
## Other panel sections
| Section | Purpose |
|---------|---------|
| **Mock Timing Presets** | Interval for mock refresh timestamps (30 sec 10 min). |
| **Trigger Mock Refresh** | Applies synthetic future timestamps locally — no backend call. |
| **Wakeup Ping Simulator** | Runs the production push handler with a synthetic `WAKEUP_PING` payload (no FCM, no backend). |
| **Flood Test** | Runs 20 sequential mock refreshes (stress test). |
| **Pending Notification Inspector** | Lists locally scheduled notifications (iOS; Android may show unavailable). |
| **Clear Notifications** | Clears/cancels all plugin-scheduled notifications on native. |
---
## Programmatic override (optional)
From a WebView dev console (`chrome://inspect` on Android, Safari Web Inspector on iOS):
```javascript
import {
setBackendBaseUrl,
setTestMode,
setBypassAuth,
getNotificationApiBaseUrl,
} from "@/services/notifications";
setBackendBaseUrl("https://abc123.ngrok-free.app");
setTestMode(true);
setBypassAuth(true); // local dev only
getNotificationApiBaseUrl();
```
---
## Troubleshooting
### 401 Unauthorized (`registerToken failed: unauthorized`)
**Likely causes:** JWT required but **Skip JWT Authentication** is off and the session is missing or expired; or JWT sent but the server rejected it.
**Checks:**
1. Panel **Backend Status**`bypassAuth: false` for hosted servers.
2. App has an active DID and endorser login.
3. Event Log: look for `Using authenticated notification request` vs `Using debug unauthenticated notification request`.
4. For hosted test server: keep **Skip JWT Authentication** **OFF**.
**Fixes:** Sign in / restore identity; refresh endorser session; for local ngrok without JWT support, enable **Skip JWT Authentication**.
### `registerToken auth unavailable` / `Waiting for auth before registration`
The app deferred registration because JWT could not be built (no active DID or empty token) and **Skip JWT Authentication** is **off**.
**Fixes:** Complete identity setup in the app, or enable **Skip JWT Authentication** only for an intentionally unauthenticated local backend.
### Failed to fetch / network error
**Likely causes:** Backend down, wrong URL, stale ngrok tunnel, device offline, or TLS/certificate issues.
**Checks:** Panel **Backend Status** URL; `curl -sS "$URL/health"` from your machine; ngrok inspect UI for incoming requests.
**Fixes:** Restart backend and ngrok; **Save Backend URL** with the current HTTPS forwarding URL (no trailing slash).
### Backend unreachable / no requests in ngrok
Same as above. Confirm the **Active** URL in the panel matches your running tunnel or local server port.
### Register succeeds but Send Real WAKEUP_PING does not trigger refresh
**Real WAKEUP_PING success** only means the backend accepted the wakeup request and attempted FCM delivery. Missing `pushNotificationReceived` / `Refresh completed (WAKEUP_PING)` indicates an FCM delivery or background execution issue — not necessarily a bad wakeup API call.
**Checks:** App backgrounded (not force-stopped); FCM token matches registration; **Simulate WAKEUP_PING (Local)** works (isolates FCM from refresh API).
See platform-specific guides for extended ngrok and FCM workflows:
- [local-android-testing-ngrok.md](./local-android-testing-ngrok.md)
- [local-ios-testing-ngrok.md](./local-ios-testing-ngrok.md)
---
## Key source files
| File | Purpose |
|------|---------|
| `src/components/dev/NotificationDebugPanel.vue` | Dev UI |
| `src/services/notifications/NotificationDebugConfig.ts` | Backend URL, testMode, bypassAuth persistence |
| `src/services/notifications/notificationApiAuth.ts` | JWT vs unauthenticated headers |
| `src/services/notifications/notificationApiDebugMode.ts` | Auth bypass gate |
| `src/services/notifications/NotificationDebugService.ts` | Panel action handlers |
| `src/services/notifications/NotificationService.ts` | `POST /notifications/register` |
| `src/services/notifications/NativeNotificationService.ts` | `POST /notifications/refresh`, WAKEUP_PING handler |
---
## Related docs
- [notification-system-overview.md](./notification-system-overview.md)
- [notification-from-api-call.md](./notification-from-api-call.md)
- [local-android-testing-ngrok.md](./local-android-testing-ngrok.md)
- [local-ios-testing-ngrok.md](./local-ios-testing-ngrok.md)