docs: document plugin vs backend authentication boundaries
Clarify that the native Daily Notification Plugin does not authenticate users or call notification-wakeup-service directly. Authentication belongs in the host app; the plugin only schedules local notifications and orchestrates host-provided content fetch. Add docs/security-boundaries.md with responsibility matrix, auth flow diagram, SPI boundary notes, and a warning against adding backend auth logic to the plugin layer.
This commit is contained in:
228
docs/security-boundaries.md
Normal file
228
docs/security-boundaries.md
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
# Security Boundaries — Native Plugin vs Backend Auth
|
||||||
|
|
||||||
|
**Purpose:** Document intentional architectural boundaries so
|
||||||
|
contributors do not add backend authentication into the plugin layer.
|
||||||
|
|
||||||
|
**Owner:** Development Team
|
||||||
|
**Last Updated:** 2026-05-20
|
||||||
|
**Status:** active
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
The **Daily Notification Plugin** is a **local notification
|
||||||
|
orchestration layer**. It schedules alarms, manages permissions, caches
|
||||||
|
content for offline delivery, and invokes **host-provided** fetch logic
|
||||||
|
when configured.
|
||||||
|
|
||||||
|
It is **not** an authentication client and **not** a backend service
|
||||||
|
client for TimeSafari identity or session management.
|
||||||
|
|
||||||
|
**Authentication belongs in the host app**, which talks to
|
||||||
|
**notification-wakeup-service**. The plugin must never own that flow.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Responsibility Matrix
|
||||||
|
|
||||||
|
| Concern | Plugin | Host app | Backend
|
||||||
|
(notification-wakeup-service) |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| User login / session | No | Yes | Yes |
|
||||||
|
| JWT / token issuance | No | Yes (or via backend) | Yes |
|
||||||
|
| Token refresh | No | Yes | Yes |
|
||||||
|
| DID / identity selection | No | Yes | May validate |
|
||||||
|
| Schedule local notifications | Yes | Configures via API | No |
|
||||||
|
| OS permission prompts | Yes | May explain UX | No |
|
||||||
|
| Local SQLite / cache | Yes | No | No |
|
||||||
|
| Prefetch timing (WorkManager, BG tasks) | Yes | No | No |
|
||||||
|
| Fetch notification **content** | Orchestrates only | Implements fetcher | Serves API data |
|
||||||
|
| Business API contracts | No | Yes | Yes |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Layer Responsibilities
|
||||||
|
|
||||||
|
### Plugin (`@timesafari/daily-notification-plugin`)
|
||||||
|
|
||||||
|
The plugin **does**:
|
||||||
|
|
||||||
|
- Schedule and cancel **local** notifications (AlarmManager,
|
||||||
|
UNUserNotificationCenter, etc.).
|
||||||
|
- Persist schedules, cached content, and delivery metadata locally.
|
||||||
|
- Run background **prefetch orchestration** (when to fetch, retries,
|
||||||
|
TTL-at-fire).
|
||||||
|
- Request and track **notification permission** status.
|
||||||
|
- Expose a **Service Provider Interface (SPI)** so the host app can
|
||||||
|
supply content in native code (`NativeNotificationContentFetcher`).
|
||||||
|
- Forward **already-obtained** credentials to a registered native
|
||||||
|
fetcher via `configureNativeFetcher()` (passthrough only; see below).
|
||||||
|
- Perform **generic HTTP GET** only when a schedule supplies an
|
||||||
|
explicit `contentFetch.url` (no login, no token refresh, no
|
||||||
|
TimeSafari-specific auth).
|
||||||
|
|
||||||
|
The plugin **does not**:
|
||||||
|
|
||||||
|
- Authenticate users.
|
||||||
|
- Communicate with **notification-wakeup-service** or other backend
|
||||||
|
auth endpoints **as an auth client**.
|
||||||
|
- Sign JWTs, manage refresh tokens, or implement OAuth/OIDC flows.
|
||||||
|
- Encode TimeSafari-specific identity or API contracts in plugin code.
|
||||||
|
|
||||||
|
### Backend (`notification-wakeup-service` and related APIs)
|
||||||
|
|
||||||
|
The backend **does**:
|
||||||
|
|
||||||
|
- Authenticate users and establish sessions (exact mechanism is owned
|
||||||
|
by the TimeSafari platform).
|
||||||
|
- Issue, validate, and refresh credentials used for API access.
|
||||||
|
- Expose wakeup / notification-related APIs consumed by the **app**,
|
||||||
|
not by the plugin directly.
|
||||||
|
|
||||||
|
The backend **does not**:
|
||||||
|
|
||||||
|
- Depend on the Capacitor plugin for login or session lifecycle.
|
||||||
|
- Assume the plugin will call auth endpoints on its own.
|
||||||
|
|
||||||
|
### Host app (TimeSafari / consuming Capacitor application)
|
||||||
|
|
||||||
|
The app **does**:
|
||||||
|
|
||||||
|
- Authenticate the user against **notification-wakeup-service**
|
||||||
|
(and any related identity services).
|
||||||
|
- Obtain and refresh tokens **before** background fetch runs.
|
||||||
|
- Register `NativeNotificationContentFetcher` in native code and
|
||||||
|
implement `fetchContent()` using app-owned HTTP clients and auth.
|
||||||
|
- Call plugin APIs to configure schedules, permissions, and optional
|
||||||
|
`configureNativeFetcher()` with **pre-generated** tokens from app code.
|
||||||
|
- Handle auth failures (401/403), logout, and identity changes without
|
||||||
|
expecting the plugin to recover sessions.
|
||||||
|
|
||||||
|
The app **does not**:
|
||||||
|
|
||||||
|
- Assume the plugin will log in or refresh credentials on its behalf.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Authentication Flow (Authoritative)
|
||||||
|
|
||||||
|
Authentication **does not** go through the plugin.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant User
|
||||||
|
participant App as Host app
|
||||||
|
participant Auth as notification-wakeup-service
|
||||||
|
participant Plugin as Daily Notification Plugin
|
||||||
|
participant OS as OS notification APIs
|
||||||
|
|
||||||
|
User->>App: Sign in / unlock identity
|
||||||
|
App->>Auth: Authenticate (credentials, DID, etc.)
|
||||||
|
Auth-->>App: Session / tokens
|
||||||
|
App->>App: Build or obtain JWT for API fetch
|
||||||
|
App->>Plugin: configureNativeFetcher(tokens from app)
|
||||||
|
App->>Plugin: registerNativeFetcher(app implementation)
|
||||||
|
Plugin->>Plugin: Schedule prefetch / notify alarms
|
||||||
|
Plugin->>App: NativeNotificationContentFetcher.fetchContent()
|
||||||
|
Note over App: App uses tokens from Auth;<br/>not plugin auth logic
|
||||||
|
App-->>Plugin: NotificationContent list
|
||||||
|
Plugin->>OS: Show local notification
|
||||||
|
```
|
||||||
|
|
||||||
|
**Rule:** `App → notification-wakeup-service` for authentication.
|
||||||
|
The plugin sits **below** that boundary and only receives outputs the
|
||||||
|
app chooses to pass in (e.g. a JWT string for a registered fetcher).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Native Content Fetcher (SPI Boundary)
|
||||||
|
|
||||||
|
Background workers **cannot** rely on JavaScript bridges. The plugin
|
||||||
|
calls **`NativeNotificationContentFetcher`**, which the **host app
|
||||||
|
implements and registers**.
|
||||||
|
|
||||||
|
- **Plugin:** When to fetch, timeouts, retries, cache write, notify
|
||||||
|
scheduling.
|
||||||
|
- **Host fetcher:** How to call APIs, which headers to send, how to
|
||||||
|
handle 401 and refresh **in app code**.
|
||||||
|
|
||||||
|
Example pattern (conceptual): the fetcher uses a token the app already
|
||||||
|
minted; the plugin never signs JWTs. See
|
||||||
|
`android/.../NativeNotificationContentFetcher.java` Javadoc.
|
||||||
|
|
||||||
|
`configureNativeFetcher()` stores or forwards `apiBaseUrl`, `activeDid`,
|
||||||
|
and `jwtToken` supplied by the app. It is **configuration passthrough**,
|
||||||
|
not an authentication implementation. Tokens are **not** persisted by
|
||||||
|
default (`persistToken` defaults to `false`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Explicit Non-Goals (Plugin)
|
||||||
|
|
||||||
|
The following are **intentionally out of scope** for this repository:
|
||||||
|
|
||||||
|
1. **User authentication** — no login UI, no credential storage for
|
||||||
|
platform identity, no session store tied to wakeup-service.
|
||||||
|
2. **Direct backend auth traffic** — the plugin must not implement
|
||||||
|
clients for `notification-wakeup-service` auth/login/refresh routes.
|
||||||
|
3. **Backend business logic** — plans, offers, projects, and community
|
||||||
|
rules live in app + backend, not in the plugin.
|
||||||
|
|
||||||
|
The plugin’s job is **local reliability**: prefetch → cache → schedule →
|
||||||
|
display.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Warning — Do Not Add Backend Auth to the Plugin
|
||||||
|
|
||||||
|
```text
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ DO NOT add backend authentication logic to the plugin │
|
||||||
|
│ layer (TypeScript bridge, Android Kotlin/Java, iOS Swift). │
|
||||||
|
│ │
|
||||||
|
│ Wrong: JWT signing, refresh flows, or HTTP clients to │
|
||||||
|
│ notification-wakeup-service auth endpoints inside │
|
||||||
|
│ DailyNotificationPlugin / FetchWorker / iOS plugin│
|
||||||
|
│ │
|
||||||
|
│ Right: Implement auth in the host app; register a native │
|
||||||
|
│ fetcher; pass short-lived tokens from app code. │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why this matters:**
|
||||||
|
|
||||||
|
- **Security:** Auth secrets and refresh logic stay in one place (the
|
||||||
|
app), subject to app review and platform keychains.
|
||||||
|
- **Coupling:** The plugin stays reusable across apps and test harnesses.
|
||||||
|
- **Background limits:** OS background tasks must stay short; auth
|
||||||
|
refresh belongs in app lifecycle, not plugin workers.
|
||||||
|
- **Audit clarity:** Security reviewers can treat wakeup-service + app
|
||||||
|
as the trust boundary; the plugin is untrusted for identity.
|
||||||
|
|
||||||
|
If integration docs elsewhere mention “plugin provides authentication,”
|
||||||
|
treat that as **orchestration of fetch timing**, not **identity
|
||||||
|
authentication**. This document is the security boundary reference.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Related Documentation
|
||||||
|
|
||||||
|
- `doc/integration/REFACTOR_NOTES.md` — SPI refactor; host-owned fetch
|
||||||
|
- `doc/integration/REFACTOR_NOTES_QUICK_START.md` — “JWT generation in
|
||||||
|
host app only”
|
||||||
|
- `android/.../NativeNotificationContentFetcher.java` — SPI contract
|
||||||
|
- `ARCHITECTURE.md` — broader system design (encryption, local storage)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Assumptions & Limits
|
||||||
|
|
||||||
|
- **notification-wakeup-service** is named as the TimeSafari auth/wakeup
|
||||||
|
entry point per platform architecture; endpoint details live in app
|
||||||
|
and backend repos, not here.
|
||||||
|
- Generic `contentFetch.url` HTTP GET in the plugin is a **legacy /
|
||||||
|
optional** path for URL-configured schedules; new integrations should
|
||||||
|
prefer the native fetcher SPI with app-owned auth.
|
||||||
|
- This document does not change runtime behavior; it records existing
|
||||||
|
intent for reviewers and implementers.
|
||||||
Reference in New Issue
Block a user