diff --git a/src/backend/server.test.ts b/src/backend/server.test.ts index 8afdf8b7b..53d8c1ba2 100644 --- a/src/backend/server.test.ts +++ b/src/backend/server.test.ts @@ -272,6 +272,29 @@ describe('PuterServer host header validation', () => { ); }); + it('lets a cross-origin preflight ask for the device fingerprint header', async () => { + // `fingerprint.ts` reads the device fingerprint off + // `x-puter-device-fingerprint` for authenticated requests with no body + // to carry it. That channel only exists if the preflight admits the + // header: a browser abandons the request otherwise, and the call never + // leaves it. + const res = await request( + '/healthcheck', + { + host: `api.puter.localhost:${port}`, + origin: 'http://puter.localhost', + 'access-control-request-method': 'GET', + 'access-control-request-headers': + 'authorization,x-puter-device-fingerprint', + }, + 'OPTIONS', + ); + expect(res.status).toBe(200); + expect( + String(res.headers['access-control-allow-headers']).toLowerCase(), + ).toContain('x-puter-device-fingerprint'); + }); + it('still short-circuits OPTIONS preflight off the dav subdomain', async () => { const res = await request( '/some-path', diff --git a/src/backend/server.ts b/src/backend/server.ts index 2e604a8b4..8602270b4 100644 --- a/src/backend/server.ts +++ b/src/backend/server.ts @@ -685,6 +685,12 @@ export class PuterServer { 'X-Expected-Entity-Length', 'DAV', 'stripe-signature', + // The GUI's fallback channel for the device fingerprint on + // authenticated requests that have no body to carry it (see + // core/http/middleware/fingerprint.ts). Without it here the + // preflight refuses the header and the request never leaves the + // browser. + 'x-puter-device-fingerprint', ].join(', '); // What a browser DAV client is allowed to read back off a response.