fix(http): exclude withCredentials requests from transfer cache

Update the transfer cache check to safely exclude all requests sent with the `withCredentials` flag.

By default, the HTTP transfer cache avoids caching user-specific responses to prevent sensitive data exposure or incorrect caching. While requests with explicit headers like `Cookie` or `Authorization` are excluded by default, requests can also be sent with credentials via the `withCredentials` flag without having those headers explicitly declared on the request object.

To keep user-specific responses from being cached, exclude `withCredentials` requests unconditionally, even when the `includeRequestsWithAuthHeaders` option is set to true.
This commit is contained in:
Yenya030
2026-05-26 17:04:08 -05:00
committed by Alex Rickabaugh
parent 4233188d8e
commit de7b2a62e7
3 changed files with 26 additions and 3 deletions
+2 -2
View File
@@ -362,7 +362,7 @@ To configure this, update your `angular.json` file as follows:
You can customize how Angular caches HTTP responses during server‑side rendering (SSR) and reuses them during hydration by configuring `HttpTransferCacheOptions`.
This configuration is provided globally using `withHttpTransferCacheOptions` inside `provideClientHydration()`.
By default, `HttpClient` caches all `HEAD` and `GET` requests which don't contain `Authorization`, `Proxy-Authorization`, or `Cookie` headers. You can override those settings by using `withHttpTransferCacheOptions` to the hydration configuration.
By default, `HttpClient` caches all `HEAD` and `GET` requests which don't contain `Authorization`, `Proxy-Authorization`, or `Cookie` headers and are not sent with `withCredentials`. You can override those settings by using `withHttpTransferCacheOptions` to the hydration configuration.
```ts
import { bootstrapApplication } from '@angular/platform-browser';
@@ -417,7 +417,7 @@ Use this only when `POST` requests are **idempotent** and safe to reuse between
### `includeRequestsWithAuthHeaders`
Determines whether requests containing `Authorization`, `Proxy‑Authorization`, or `Cookie` headers are eligible for caching.
By default, these are excluded to prevent caching user‑specific responses.
By default, these are excluded to prevent caching user‑specific responses. Requests sent with `withCredentials` are also excluded by default.
```ts
withHttpTransferCacheOptions({
+3 -1
View File
@@ -42,7 +42,7 @@ import {HttpParams} from './params';
* (for example using GraphQL).
* @param includeRequestsWithAuthHeaders Enables caching of requests containing `Authorization`,
* `Proxy-Authorization`, or `Cookie` headers. By default, these requests are excluded from
* caching.
* caching. Requests sent using `withCredentials` are also excluded by default.
*
* @see [Configuring the caching options](guide/ssr#configuring-the-caching-options)
*
@@ -135,6 +135,8 @@ export function transferCacheInterceptorFn(
if (
!isCacheActive ||
requestOptions === false ||
// Do not cache requests sent with credentials.
req.withCredentials ||
// POST requests are allowed either globally or at request level
(requestMethod === 'POST' && !globalOptions.includePostRequests && !requestOptions) ||
(requestMethod !== 'POST' && !ALLOWED_METHODS.includes(requestMethod)) ||
@@ -40,6 +40,7 @@ interface RequestParams {
observe?: 'body' | 'response';
transferCache?: {includeHeaders: string[]} | boolean;
headers?: {[key: string]: string};
withCredentials?: boolean;
body?: RequestBody;
}
@@ -397,6 +398,16 @@ describe('TransferCache', () => {
makeRequestAndExpectOne('/test-auth', 'foo');
});
it('should not cache requests with credentials', async () => {
makeRequestAndExpectOne('/test-auth', 'foo', {
withCredentials: true,
});
makeRequestAndExpectOne('/test-auth', 'foo', {
withCredentials: true,
});
});
it('should cache POST with the differing body in string form', () => {
makeRequestAndExpectOne('/test-1', null, {method: 'POST', transferCache: true, body: 'foo'});
makeRequestAndExpectNone('/test-1', 'POST', {transferCache: true, body: 'foo'});
@@ -549,6 +560,16 @@ describe('TransferCache', () => {
makeRequestAndExpectNone('/test-auth');
});
it(`should not cache requests with credentials when 'includeRequestsWithAuthHeaders' is 'true'`, async () => {
makeRequestAndExpectOne('/test-auth', 'foo', {
withCredentials: true,
});
makeRequestAndExpectOne('/test-auth', 'foo', {
withCredentials: true,
});
});
it('should cache a POST request', () => {
makeRequestAndExpectOne('/include?foo=1', 'post-body', {method: 'POST'});