# Security Implications of FadCam's Remote Streaming: Token-Based Authentication and E2E Encryption

> Explore FadCam's remote streaming security: short-lived tokens, JWTs, and E2E encryption prevent unauthorized access and data exposure. Learn about its defense-in-depth approach.

- Repository: [Faded/FadCam](https://github.com/anonfaded/FadCam)
- Tags: security
- Published: 2026-05-13

---

**FadCam's remote streaming implements defense-in-depth through short-lived handoff tokens, session-isolated JWTs, and optional end-to-end encryption to prevent unauthorized access, replay attacks, and server-side data exposure.**

FadCam is an open-source Android surveillance application that supports remote streaming via cloud infrastructure. Understanding the security implications of FadCam's remote streaming requires analyzing its token-based authentication flow and cryptographic safeguards implemented in the web-based remote interface. The architecture deliberately isolates cloud streaming from local LAN access through strict hostname validation and multi-layered session management.

## Authentication Flow and Token Exchange

### Handoff Token to Session Exchange

The remote streaming initiation relies on a **single-use handoff token** exchanged for persistent session credentials. When a user clicks "Open Stream" in the Lab interface, the system generates a handoff JWT embedded in the URL (`?token=`). The `FadCamRemote.exchangeHandoffToken()` method in [`app/src/main/assets/web/js/fadcam-remote.js`](https://github.com/anonfaded/FadCam/blob/main/app/src/main/assets/web/js/fadcam-remote.js) immediately exchanges this token via the Supabase Edge Function `exchange-handoff-token`.

Upon successful exchange, the function returns a **session hint**, **user record**, **stream-access JWT**, and an optional **E2E verify tag**. The client immediately sanitizes the URL using `cleanUrl.searchParams.delete('token')` and `window.history.replaceState` to ensure the handoff token never persists in browser history or the address bar.

### Stream-Access JWT Management

The stream-access JWT is stored in `localStorage` under the key `CLOUD_CONFIG.STORAGE_KEYS.STREAM_TOKEN`, not in cookies or URL parameters. The `FadCamRemote.getStreamToken()` method retrieves this value when constructing HLS requests. This design prevents cross-site request forgery (CSRF) attacks and ensures tokens are only sent to explicitly configured streaming origins.

## Session Validation and Security Checks

### Server-Side Token Verification

Before reusing any cached session, `FadCamRemote.initStreamContext()` validates the JWT against the Supabase Edge Function `verify-stream-token`. This server-side check ensures the token has not been revoked or expired due to policy changes. If the server returns **401 Unauthorized**, the client immediately wipes all stored credentials from `localStorage` and redirects to the Lab login page.

### Client-Side Expiry Handling

As a defense against clock skew or temporary offline usage, the client parses the JWT payload using `atob(parts[1])` and validates the `exp` claim locally. If `initStreamContext()` detects an expired token client-side, it proactively clears `CLOUD_CONFIG.STORAGE_KEYS.SESSION` and `CLOUD_CONFIG.STORAGE_KEYS.STREAM_TOKEN`, forcing a fresh authentication flow before any network requests expose the stale token.

## End-to-End Encryption Architecture

For streams marked with an `e2e-verify-tag`, FadCam implements **end-to-end encryption** of HLS fragments. The `FadCamRemote.checkAndShowE2EUnlock()` method checks IndexedDB for an existing decryption key via `E2EKeyManager.isInitialized()`. If absent, the UI presents a password modal that requires the user's LabPass to derive the encryption key.

The `FadCamFragLoader` class (when present in [`app/src/main/assets/web/js/services/FadCamFragLoader.js`](https://github.com/anonfaded/FadCam/blob/main/app/src/main/assets/web/js/services/FadCamFragLoader.js)) intercepts each media segment downloaded by HLS.js. It decrypts fragments on-the-fly using the derived key, ensuring that even if the streaming server (`live.fadseclab.com`) is compromised, the video content remains ciphertext without the user's password.

## Network Security and Access Controls

### Hostname Whitelisting

The `FadCamRemote.isWebAccess()` function enforces strict **origin validation** before enabling any cloud features. Remote streaming functionality activates only on whitelisted hostnames (`fadseclab.com`, `localhost`). LAN/IP-based accesses bypass the remote stack entirely, preventing accidental exposure of cloud tokens when accessing the device directly via local network addresses.

### Request Rate Limiting

The `HlsService` configuration in [`app/src/main/assets/web/js/services/HlsService.js`](https://github.com/anonfaded/FadCam/blob/main/app/src/main/assets/web/js/services/HlsService.js) implements **DoS protection** through `fragLoadPolicy` and `manifestLoadPolicy` settings. These policies limit retry attempts and implement exponential back-off delays when encountering 4xx or 5xx errors, preventing brute-force attacks against the token validation endpoints.

### Secure Token Cleanup

The `AuthService.logout()` method (located in [`app/src/main/assets/web/js/services/AuthService.js`](https://github.com/anonfaded/FadCam/blob/main/app/src/main/assets/web/js/services/AuthService.js)) performs complete session termination. It removes tokens from `localStorage`, clears IndexedDB encryption keys, and notifies the server to invalidate the session server-side, ensuring that stolen tokens cannot be replayed even if extracted from a compromised browser profile.

## Code Implementation Examples

### Starting a Remote Stream

```javascript
// Extract device ID from path and initialize secure context
const deviceId = FadCamRemote.getStreamDeviceId();   // → "abc123"
FadCamRemote.initStreamContext(deviceId);            // Handles token exchange and validation

```

*Source:* `FadCamRemote.initStreamContext()` in [`app/src/main/assets/web/js/fadcam-remote.js`](https://github.com/anonfaded/FadCam/blob/main/app/src/main/assets/web/js/fadcam-remote.js).

### Retrieving the HLS Playlist

```javascript
const playlistUrl = FadCamRemote.getRelayHlsUrl(); 
// → https://live.fadseclab.com:8443/stream/<user>/<device>/live.m3u8

```

*Source:* `FadCamRemote.getRelayHlsUrl()` in [`app/src/main/assets/web/js/fadcam-remote.js`](https://github.com/anonfaded/FadCam/blob/main/app/src/main/assets/web/js/fadcam-remote.js).

### Automatic JWT Injection in HLS Requests

```javascript
// Inside HlsService._getHlsConfig()
baseConfig.xhrSetup = function (xhr, url) {
  if ((url.includes('/stream/') || url.includes('live.fadseclab.com')) && !url.includes('token=')) {
    const separator = url.includes('?') ? '&' : '?';
    const authUrl = `${url}${separator}token=${encodeURIComponent(streamToken)}`;
    xhr.open('GET', authUrl, true);
  }
};

```

*Source:* `HlsService._getHlsConfig()` in [`app/src/main/assets/web/js/services/HlsService.js`](https://github.com/anonfaded/FadCam/blob/main/app/src/main/assets/web/js/services/HlsService.js).

### Handling Token Expiration

```javascript
// Server returned 401 or local expiry detected
localStorage.removeItem(CLOUD_CONFIG.STORAGE_KEYS.SESSION);
localStorage.removeItem(CLOUD_CONFIG.STORAGE_KEYS.STREAM_TOKEN);
window.location.href = CLOUD_CONFIG.LAB_URL;  // Force re-authentication

```

*Source:* `initStreamContext()` error handling in [`app/src/main/assets/web/js/fadcam-remote.js`](https://github.com/anonfaded/FadCam/blob/main/app/src/main/assets/web/js/fadcam-remote.js).

### E2E Encryption Unlock Flow

```javascript
// Check for encrypted stream and prompt for LabPass if needed
if (verifyTag && !(await E2EKeyManager.isInitialized())) {
  FadCamRemote.showE2EUnlockModal();
}

```

*Source:* `FadCamRemote.checkAndShowE2EUnlock()` in [`app/src/main/assets/web/js/fadcam-remote.js`](https://github.com/anonfaded/FadCam/blob/main/app/src/main/assets/web/js/fadcam-remote.js).

## Summary

- **Short-lived tokens**: Handoff tokens are exchanged immediately and deleted from the URL, while stream JWTs are validated on every request.
- **Defense in depth**: Server-side verification via `verify-stream-token` combined with client-side `exp` claim parsing prevents session hijacking.
- **E2E encryption**: Optional fragment-level encryption ensures video content remains inaccessible even if the streaming infrastructure is compromised.
- **Origin isolation**: Strict hostname whitelisting separates cloud and local LAN access paths, preventing token leakage to unauthorized origins.
- **Automatic cleanup**: 401 responses trigger immediate local storage wiping, ensuring revoked tokens cannot persist for replay attacks.

## Frequently Asked Questions

### How does FadCam prevent replay attacks using stolen tokens?

FadCam mitigates replay attacks through **short-lived handoff tokens** and **server-side session validation**. The initial handoff token is single-use and exchanged immediately upon page load. Subsequent stream-access JWTs are validated by the `verify-stream-token` Edge Function on every HLS segment request. If a token is revoked or expired, the server returns 401, triggering immediate client-side deletion of all session data via `localStorage.removeItem()`.

### What happens when a stream token expires during active viewing?

When the JWT `exp` claim lapses or the server returns 401, `FadCamRemote.initStreamContext()` clears both `CLOUD_CONFIG.STORAGE_KEYS.SESSION` and `CLOUD_CONFIG.STORAGE_KEYS.STREAM_TOKEN` from `localStorage`. The user is then redirected to the Lab authentication URL (`CLOUD_CONFIG.LAB_URL`) to obtain fresh credentials, ensuring no stale tokens remain in the browser.

### Is video content encrypted during remote streaming?

Remote streaming supports **optional end-to-end encryption** when the `e2e-verify-tag` is present in the session metadata. In this mode, HLS fragments are encrypted server-side and decrypted client-side by `FadCamFragLoader` using a key derived from the user's LabPass. Without the correct password, intercepted stream segments remain unreadable ciphertext, protecting against server compromise and man-in-the-middle attacks.

### How does FadCam distinguish between local and remote access?

The `FadCamRemote.isWebAccess()` function performs **strict hostname validation**, enabling cloud features only on `fadseclab.com` and `localhost`. Direct IP or local network access bypasses the remote streaming stack entirely, preventing accidental exposure of cloud authentication tokens when users access the device via LAN. This architectural separation ensures local usage never invokes `exchangeHandoffToken()` or stores cloud JWTs.