Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ and this project adheres to

### Fixed

- πŸ›(y-provider) send X-Forwarded-Proto on internal backend calls #2541
- πŸ›(frontend) redirect homepage to login when homepage feat is disabled #2521

### Changed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,29 @@ describe('CollaborationBackend', () => {

axiosGetSpy.mockRestore();
});

test('always sends X-Forwarded-Proto: https regardless of the incoming header', async () => {
const axiosGetSpy = vi.spyOn(axios, 'get').mockResolvedValue({
status: 200,
data: { id: 'test-user-id', email: 'test@example.com' },
});

const { fetchCurrentUser } = await import('@/api/collaborationBackend');

await fetchCurrentUser({
cookie: 'test-cookie',
'x-forwarded-proto': 'http',
});

expect(axiosGetSpy).toHaveBeenCalledWith(
'http://app-dev:8000/api/v1.0/users/me/',
expect.objectContaining({
headers: expect.objectContaining({
'X-Forwarded-Proto': 'https',
}),
}),
);

axiosGetSpy.mockRestore();
});
});
14 changes: 14 additions & 0 deletions src/frontend/servers/y-provider/src/api/collaborationBackend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,19 @@ interface Doc {
};
}

/**
* In production the backend sets SECURE_SSL_REDIRECT and only trusts
* X-Forwarded-Proto to tell whether TLS was terminated upstream. This call is
* internal, so when COLLABORATION_BACKEND_BASE_URL points at the backend
* directly it never crosses the reverse proxy: nothing sets the header, Django
* answers a 301 to https://<host>:<port>, and the client then speaks TLS to a
* port that serves plain HTTP.
*
* Always send https: it is the safe default given the current Production
* settings. Relaying the client's own X-Forwarded-Proto would let a client
* that sends X-Forwarded-Proto: http on its WebSocket upgrade force a 301 and
* break its own connection β€” a vector this fix has no reason to introduce.
*/
async function fetch<T>(
path: string,
requestHeaders: IncomingHttpHeaders,
Expand All @@ -63,6 +76,7 @@ async function fetch<T>(
cookie: requestHeaders['cookie'],
origin: requestHeaders['origin'],
'X-Y-Provider-Key': Y_PROVIDER_API_KEY,
'X-Forwarded-Proto': 'https',
},
},
);
Expand Down