# 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.
