mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(http): skip transfer cache for fetch credentialed requests
Treat HttpClient requests using `credentials: 'include'` and `same-origin` as credentialed when deciding whether a response can be stored in the HTTP transfer cache.
The transfer cache already skips requests with `withCredentials`, `Cookie`, `Authorization`, or `Proxy-Authorization` because those responses may contain user-specific data. Fetch-backed requests can express the same credentialed behavior through the `credentials` option, so these responses must not be serialized into the SSR HTML.
This keeps credentialed SSR responses out of TransferState and aligns the cache eligibility check with the fetch request options supported by HttpClient.
(cherry picked from commit 8ec01970d2)
This commit is contained in:
@@ -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 and are not sent with `withCredentials`. 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` or Fetch API `credentials` modes that can send credentials. 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. Requests sent with `withCredentials` are also excluded by default.
|
||||
By default, these are excluded to prevent caching user‑specific responses. Requests sent with `withCredentials` or Fetch API `credentials` set to `include` or `same-origin` are also excluded by default.
|
||||
|
||||
```ts
|
||||
withHttpTransferCacheOptions({
|
||||
|
||||
@@ -42,7 +42,8 @@ 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. Requests sent using `withCredentials` are also excluded by default.
|
||||
* caching. Requests sent using `withCredentials` or Fetch API `credentials` modes that can send
|
||||
* credentials are also excluded by default.
|
||||
*
|
||||
* @see [Configuring the caching options](guide/ssr#configuring-the-caching-options)
|
||||
*
|
||||
@@ -136,7 +137,7 @@ export function transferCacheInterceptorFn(
|
||||
!isCacheActive ||
|
||||
requestOptions === false ||
|
||||
// Do not cache requests sent with credentials.
|
||||
req.withCredentials ||
|
||||
hasOutgoingCredentials(req) ||
|
||||
// POST requests are allowed either globally or at request level
|
||||
(requestMethod === 'POST' && !globalOptions.includePostRequests && !requestOptions) ||
|
||||
(requestMethod !== 'POST' && !ALLOWED_METHODS.includes(requestMethod)) ||
|
||||
@@ -253,6 +254,10 @@ function hasAuthHeaders(req: HttpRequest<unknown>): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
function hasOutgoingCredentials(req: HttpRequest<unknown>): boolean {
|
||||
return req.withCredentials || req.credentials === 'include' || req.credentials === 'same-origin';
|
||||
}
|
||||
|
||||
function getFilteredHeaders(
|
||||
headers: HttpHeaders,
|
||||
includeHeaders: string[] | undefined,
|
||||
|
||||
@@ -41,6 +41,7 @@ interface RequestParams {
|
||||
transferCache?: {includeHeaders: string[]} | boolean;
|
||||
headers?: {[key: string]: string};
|
||||
withCredentials?: boolean;
|
||||
credentials?: RequestCredentials;
|
||||
body?: RequestBody;
|
||||
}
|
||||
|
||||
@@ -408,6 +409,36 @@ describe('TransferCache', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should not cache requests with included credentials', async () => {
|
||||
makeRequestAndExpectOne('/test-auth', 'foo', {
|
||||
credentials: 'include',
|
||||
});
|
||||
|
||||
makeRequestAndExpectOne('/test-auth', 'foo', {
|
||||
credentials: 'include',
|
||||
});
|
||||
});
|
||||
|
||||
it('should not cache requests with same-origin credentials', async () => {
|
||||
makeRequestAndExpectOne('/test-auth', 'foo', {
|
||||
credentials: 'same-origin',
|
||||
});
|
||||
|
||||
makeRequestAndExpectOne('/test-auth', 'foo', {
|
||||
credentials: 'same-origin',
|
||||
});
|
||||
});
|
||||
|
||||
it('should cache requests with omitted credentials', async () => {
|
||||
makeRequestAndExpectOne('/test-auth', 'foo', {
|
||||
credentials: 'omit',
|
||||
});
|
||||
|
||||
makeRequestAndExpectNone('/test-auth', 'GET', {
|
||||
credentials: 'omit',
|
||||
});
|
||||
});
|
||||
|
||||
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'});
|
||||
@@ -570,6 +601,16 @@ describe('TransferCache', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it(`should not cache requests with included credentials when 'includeRequestsWithAuthHeaders' is 'true'`, async () => {
|
||||
makeRequestAndExpectOne('/test-auth', 'foo', {
|
||||
credentials: 'include',
|
||||
});
|
||||
|
||||
makeRequestAndExpectOne('/test-auth', 'foo', {
|
||||
credentials: 'include',
|
||||
});
|
||||
});
|
||||
|
||||
it('should cache a POST request', () => {
|
||||
makeRequestAndExpectOne('/include?foo=1', 'post-body', {method: 'POST'});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user