firebase / firebase/firebase-tools
Automatically deployed cloud function strips headers breaking any attempt at authentication
- Dominant language
- TypeScript
- Stars
- 4.5k
- Forks
- 1.3k
- Avg merge
- 1d 12h
- Merged PRs (30d)
- 84
Description
### [REQUIRED] Environment info
**firebase-tools:**
firebase --version
13.16.0
**Platform:**
uname -a
Darwin MYHOSTNAME.localdomain 23.6.0 Darwin Kernel Version 23.6.0: Mon Jul 29 21:14:30 PDT 2024; root:xnu-10063.141.2~1/RELEASE_ARM64_T6000 arm64
### [REQUIRED] Test case
This is difficult to minimise.
1. Use sveltekit
2. Add a login page that's sets the session cookie server side
3. Ensure users on the site are verified and redirect them to the login page if they're not
```svelte
export const actions: Actions = {
login: async ({ request, cookies, locals }) => {
const formData = await request.formData();
const idToken = formData.get('idToken');
if (!idToken) {
return {
status: 401,
body: { error: 'Unauthorized' }
};
}
try {
// Verify the ID token
const decodedIdToken = await adminAuth.verifyIdToken(idToken.toString());
// Create a session cookie
const sessionCookie = await adminAuth.createSessionCookie(idToken.toString(), {
expiresIn: 60 * 60 * 24 * 5 * 1000
}); // 5 days
// Set the session cookie in the response
cookies.set('session', sessionCookie, {
path: '/',
maxAge: 60 * 60 * 24 * 5 * 1000, // 5 days
httpOnly: true,
secure: true,
sameSite: 'none',
domain: 'MYDOMAIN.com'
});
locals.session = sessionCookie;
return {
status: 200,
headers: {
session: sessionCookie
},
body: { user: decodedIdToken }
};
} catch (error) {
console.error('Failed to create session cookie:', error);
return {
status: 401,
body: { error: 'Unauthorized' }
};
}
}
}
```
On the `hooks.server.ts` side, ensure there is a session that's verified, otherwise redirect.
```svelte
// A lot of the console.errors is to allow me to trace the behaviour in the function logs.
// The multiple attempts to retrieve a session is a shotgun approach to make this work on firebase without luck
export async function handle({ event, resolve }) {
let session =
event.locals.session || event.cookies.get('session')
console.error('session:', session);
if (!session) {
const cookieHeader = event.request.headers.get('cookie');
console.error('cookieHeader:', cookieHeader);
if (cookieHeader) {
// Parse the Cookie header manually to extract the session cookie
const cookies = cookieHeader.split(';').reduce((acc, cookie) => {
const [key, value] = cookie.trim().split('=');
acc[key] = value;
return acc;
}, {});
console.error('cookies:', cookies);
session = cookies['session'];
}
}
if (!session && event.url.pathname !== '/login') {
console.error('No session cookie found');
return new Response(null, { status: 302, headers: { location: '/login' } });
}
if (session && event.url.pathname !== '/login') {
try {
const decodedClaims = await adminAuth.verifySessionCookie(session);
event.locals.user = decodedClaims;
} catch (error) {
Sentry.captureException(error);
console.error('Error verifying session cookie:', error);
// disable right now otherwise with the current issues this will loop indefinitley.
// return new Response(null, { status: 302, headers: { location: '/login' } });
}
}
const response = await resolve(event);
return response;
}
```
### [REQUIRED] Steps to reproduce
1. In addition to setting up sveltekit and the above authentication code,
2. Ensure that firebase auth is set-up correctly
3. Ensure that the /login/+page.svelte page calls `auth.signIn()`
4. Use `frameworksBackend`, see full `firebase.json` file below
5. Browse to any `SSR` url
6. Verify the session gets sent in the dev tools in your browser
7. Verify that the headers never get passed onto your server-side code through the logs.
**Note** the rewrite is needed to ensure the client sends the `session` cookie, without it uses a cross-domain URL and the session never gets sent across.
```json
{
"hosting": {
"source": ".",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"frameworksBackend": {
"region": "europe-west1"
},
"rewrites": [
{
"source": "**",
"function": "MYFUNCTIONAME"
}
]
}
}
```
### [REQUIRED] Expected behavior
hooks.server.ts should not redirect me
The session should be passed onto my server side code.
At a minimum, the headers should not be cleared by firebase.
### [REQUIRED] Actual behavior
`
DEFAULT 2024-09-11T08:07:47.109493Z session: undefined
DEFAULT 2024-09-11T08:07:47.109652Z cookieHeader: null
DEFAULT 2024-09-11T08:07:47.109685Z No session cookie found
`
Contributor guide
Assessment
This issue has not been assessed yet.