firebase / firebase/firebase-tools

Firebase hosting deploy issue with Nextjs 15.1.3 : request cookies are null in the middleware

Open
#8,099 2 comments 0 reactions 0 assignees View on GitHub
api: hosting type: bug
Dominant language
TypeScript
Stars
4.5k
Forks
1.3k
Avg merge
1d 12h
Merged PRs (30d)
84

Description

### [REQUIRED] Environment info

**firebase-tools:** 13.29.1

**Platform:** macOS

### [REQUIRED] Test case

### [REQUIRED] Steps to reproduce

I have a Nextjs 15.1.3 website that works well in development mode (locally). However, when deployed to Firebase hosting, I am no longer able to read cookies in the middleware for GET requests. POST requests are fine.

Here is what I am trying to do:

When the user signs in, I get the token and refresh token. Then, I call my Server Action function "setToken()" to set the cookies in the server.
as shown below:

```
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(async (user) => {
setCurrentUser(user ?? null);
if (user) {
// get the token and refresh token
const tokenResult = await user.getIdTokenResult();
const token = tokenResult.token;
const refreshToken = user.refreshToken;

if (token && refreshToken) {
// Call server action with token and refresh token
await setToken({token, refreshToken});
}
const claims = tokenResult.claims;
setCustomClaims(claims ?? null);
} else {
await removeToken();
}
});

return () => unsubscribe();
}, []);
```

Here is what the setToken() server action looks like. It sets the cookies server-side

```
export const setToken = async ({
token,
refreshToken,
}: {
token: string;
refreshToken: string;
}) => {
try {
const verifiedToken = await auth.verifyIdToken(token);
if (!verifiedToken) {
return;
}

const userRecord = await auth.getUser(verifiedToken.uid);

if (process.env.ADMIN_EMAIL === userRecord.email) {

auth.setCustomUserClaims(verifiedToken.uid, {
role: 'admin',
});

// this is where the cookies are set server side
const cookieStore = await cookies();
cookieStore.set('firebaseAuthToken', token, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
});
cookieStore.set('firebaseAuthRefreshToken', refreshToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
});

}
} catch (e) {
console.log(e);
}
};
```

In the middleware, I try to read the cookie but it is null in production (Firebase hosting). In development (locally) is it not null. Here is what the middleware looks like:

```
export async function middleware(request: NextRequest) {
const pathname = request.nextUrl.pathname;

if (request.method === 'POST') {
return NextResponse.next();
}

const cookieStore = await cookies();
const token = cookieStore.get('firebaseAuthToken')?.value;

if (pathname !== '/login') {
if (!token) {
return NextResponse.redirect(new URL('/login', request.url));
}

const decodedToken = decodeJwt(token);

if (!isAutherized(decodedToken.role as string | undefined)) {
return NextResponse.redirect(new URL('/login', request.url));
}

return NextResponse.next();
} else {
if (token) {
const decodedToken = decodeJwt(token);

if (!isAutherized(decodedToken.role as string | undefined)) {
return NextResponse.next();
}

return NextResponse.redirect(new URL('/messages', request.url));
}

return NextResponse.next();
}
}
```

I also noticed that I am able to read the token in the middleware if the request is a POST request but not when the request is GET request.

Here is my package.json. I had to use "firebase-admin": "^12.0.0" when deploying my site to Firebase hosting.

```
{
"private": true,
"scripts": {
"dev": "next dev --turbopack",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@hookform/resolvers": "^3.9.1",
"@radix-ui/react-alert-dialog": "^1.1.4",
"@radix-ui/react-aspect-ratio": "^1.1.1",
"@radix-ui/react-avatar": "^1.1.2",
"@radix-ui/react-dialog": "^1.1.4",
"@radix-ui/react-label": "^2.1.1",
"@radix-ui/react-navigation-menu": "^1.2.3",
"@radix-ui/react-radio-group": "^1.2.2",
"@radix-ui/react-select": "^2.1.4",
"@radix-ui/react-separator": "^1.1.1",
"@radix-ui/react-slider": "^1.2.2",
"@radix-ui/react-slot": "^1.1.1",
"@radix-ui/react-toast": "^1.2.4",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"firebase": "^11.1.0",
"firebase-admin": "^12.0.0",
"jose": "^5.9.6",
"lucide-react": "^0.469.0",
"next": "15.1.3",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-hook-form": "^7.54.2",
"tailwind-merge": "^2.6.0",
"tailwindcss-animate": "^1.0.7",
"uuid": "^11.0.3",
"zod": "^3.24.1"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^9",
"eslint-config-next": "15.1.3",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"typescript": "^5"
}
}
```

### [REQUIRED] Expected behavior

I expect to be able to read the cookie in the middleware for GET requests.

### [REQUIRED] Actual behavior
I noticed that I am only able to read the token in a POST request but not in a GET request

Contributor guide

Open the contributing guide

Research direction

Start with the Next.js middleware and the setToken server action shown in the report, then compare the cookie behavior for GET and POST requests after Firebase Hosting deployment. Check the Firebase CLI and Hosting deployment context first; done means identifying a reproducible Firebase Hosting or CLI cause, or documenting that the behavior is application configuration rather than a firebase-tools defect.

Written by the indexing model from the issue text.

Assessment

Tech stack
firebase, nextjs, typescript
Domain
authentication, cloud
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.