firebase-tools
firebase-tools copied to clipboard
Automatically deployed cloud function strips headers breaking any attempt at authentication
[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.
- Use sveltekit
- Add a login page that's sets the session cookie server side
- Ensure users on the site are verified and redirect them to the login page if they're not
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.
// 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
- In addition to setting up sveltekit and the above authentication code,
- Ensure that firebase auth is set-up correctly
- Ensure that the /login/+page.svelte page calls
auth.signIn()
- Use
frameworksBackend
, see fullfirebase.json
file below - Browse to any
SSR
url - Verify the session gets sent in the dev tools in your browser
- 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.
{
"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