You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(nextjs): add firebase-cookie-middleware with security hardening
Introduces the firebase-cookie-middleware package (v0.0.1) for
Next.js 14/15/16, taking over from PR #640 (James Daniels).
Security fixes applied during takeover review:
- Emulator explicit opt-in only (env var not auto-detected)
- JWT signature failure sets refreshable=false to prevent bypass
- Verify refreshed token before accepting it
- SSRF: loopback validation on emulator host
- CSRF: Origin check on POST/DELETE to /__cookies__
- CHIPS: SameSite=None on partitioned cookies
- JWKS eviction lock released in finally block
- Falsy-zero TTL: require ttlSeconds > 0 before caching
- Proxy targets validated; return 400 instead of throwing
- Optional chaining on JWT payload access
- Removed duplex: 'half' workaround (unnecessary in Node.js)
Next.js 16: adds proxy export (proxy.ts replaces middleware.ts
in Next.js 16 projects). proxy.ts runs on Node.js runtime only;
middleware.ts remains available for Edge Runtime (deprecated).
Also fixes pre-existing TypeScript strict-mode errors surfaced
by upgrading moduleResolution from node to bundler.
Ref: #640
Original work by: James Daniels (jamesdaniels@google.com)
Standard Single Page Applications (SPAs) store Firebase ID and refresh tokens inside browser `indexedDB` or `localStorage`. Because these storage mechanisms are inaccessible during HTTP requests, Next.js Server Components, API routes, Server Actions, and Middleware cannot read the user's authentication state on the first request—causing layout shifts, client-side redirect flashes, or insecure server routes.
11
+
Standard Single Page Applications (SPAs) store Firebase ID and refresh tokens inside browser `indexedDB` or `localStorage`. Because these storage mechanisms are inaccessible during HTTP requests, Next.js Server Components, API routes, Server Actions, and Middleware cannot read the user's authentication state on the first request, causing layout shifts, client-side redirect flashes, or insecure server routes.
12
12
13
13
`firebase-cookie-middleware` acts as the server-side companion to [`browserCookiePersistence`](https://firebase.google.com/docs/reference/js/auth#browsercookiepersistence). It proxies Firebase Auth token requests through `/_\_cookies_\_` on your app's domain, intercepts authentication exchanges, securely stores ID tokens and `httpOnly` refresh tokens in standard HTTP cookies, and strips sensitive refresh credentials from browser-facing payloads.
14
14
15
15
---
16
16
17
17
## Features
18
18
19
-
-**⚡ 100% Edge Runtime Compatible**: Engineered specifically for Next.js 14 & 15 Edge Runtimes (Vercel Edge, Cloudflare Workers). Built on `jose` and Web APIs (`atob`, `fetch`) with zero reliance on Node.js `Buffer`.
19
+
-**⚡ Next.js 14, 15, and 16 Compatible**: Built on `jose` and Web APIs (`atob`, `fetch`). Runs in the Edge Runtime via `middleware.ts` (Next.js 14/15, and Next.js 16 users keeping the deprecated Edge path) and the Node.js runtime via `proxy.ts` (Next.js 16 recommended).
20
20
-**🛡️ Seamless Route Protection & Role Checking**: Intercept and verify Firebase ID tokens at the Edge before rendering pages or API routes. Access standard claims (`email`, `sub`) and arbitrary custom claims directly in your middleware.
21
21
-**🚀 Distributed Caching (Memorystore / Redis)**: Optional distributed caching for Google's public JWKS signing keys and verified token payloads (`jwt:<idToken>`). Prevents unnecessary CPU verification and network requests on every navigation.
22
22
-**🔒 Anti-DDoS Rotation Protection**: Distributed Redis locking (`firebase:jwks_eviction_lock`) ensures that during signing key rotations, only one Edge worker re-fetches Google's JWKS endpoints, preventing rate-limiting cascades.
@@ -33,11 +33,29 @@ npm install firebase-cookie-middleware jose lru-cache
33
33
34
34
---
35
35
36
+
## Next.js 16 Migration
37
+
38
+
Next.js 16 deprecates `middleware.ts` in favor of `proxy.ts`. The named export also changes from `middleware` to `proxy`.
39
+
40
+
**Next.js 15 and earlier** (`src/middleware.ts`, Edge Runtime):
`proxy.ts` runs on the Node.js runtime only; the `runtime` config option is not available and will throw if set. If you need the Edge Runtime in Next.js 16, you can keep using `middleware.ts` with the `middleware` export (deprecated by Next.js but still functional). All middleware logic is identical between the two exports; only the file name and export name change.
51
+
52
+
---
53
+
36
54
## Quickstart
37
55
38
-
### 1. Create your Middleware (`src/middleware.ts`)
56
+
### 1. Create your Middleware (`src/middleware.ts` for Next.js 15, `src/proxy.ts` for Next.js 16)
39
57
40
-
Create or update your `middleware.ts` file in the root of your Next.js application:
58
+
Create or update the file in the root of your Next.js application:
@@ -210,10 +228,23 @@ The middleware maintains two distinct cookies per app configuration (`appName`):
210
228
211
229
When testing locally on `http://localhost`, Chrome and Safari reject secure host-prefixed cookies (`__HOST-`). The middleware automatically detects insecure local protocols and falls back to prefixed development cookies (`__dev_FIREBASE_[DEFAULT]`) with appropriate security flags.
212
230
213
-
To connect with the Firebase Auth Emulator, export your emulator environment variable:
231
+
To connect with the Firebase Auth Emulator, set `emulator: true` in your config and export the emulator host:
The middleware automatically accepts unsigned emulator tokens (`alg: "none"`) and proxies token refresh attempts directly to your local emulator instance.
emulator: true, // reads FIREBASE_AUTH_EMULATOR_HOST; required to accept unsigned tokens
241
+
});
242
+
```
243
+
244
+
You can also pass the host directly as a string instead of relying on the env var:
245
+
246
+
```typescript
247
+
emulator: "localhost:9099"
248
+
```
249
+
250
+
Emulator mode accepts unsigned tokens (`alg: "none"`) and proxies token refresh requests to your local emulator. The explicit opt-in is required: the env var alone does not activate emulator mode, preventing accidental acceptance of unsigned tokens if the variable leaks into a production environment.
`firebase-cookie-middleware: app "${appName}" returned a direct response but an earlier app already claimed the response. The earlier response takes precedence.`,
0 commit comments