diff --git a/adev/src/content/guide/ssr.md b/adev/src/content/guide/ssr.md index a0cc7686cde..70daba55151 100644 --- a/adev/src/content/guide/ssr.md +++ b/adev/src/content/guide/ssr.md @@ -432,7 +432,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'; @@ -487,7 +487,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({ diff --git a/packages/common/http/src/transfer_cache.ts b/packages/common/http/src/transfer_cache.ts index 9311dbac910..ab4423a7a73 100644 --- a/packages/common/http/src/transfer_cache.ts +++ b/packages/common/http/src/transfer_cache.ts @@ -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) * @@ -132,7 +133,7 @@ function canUseOrCacheRequest(req: HttpRequest, options: CacheOptions): !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)) || @@ -342,6 +343,7 @@ function hasOutgoingCredentials(req: HttpRequest): boolean { return withCredentials || credentials === 'include' || credentials === 'same-origin'; } + function getFilteredHeaders( headers: HttpHeaders, includeHeaders: string[] | undefined, diff --git a/packages/common/http/test/transfer_cache_spec.ts b/packages/common/http/test/transfer_cache_spec.ts index e30fcb04fbd..3293ccd1a47 100644 --- a/packages/common/http/test/transfer_cache_spec.ts +++ b/packages/common/http/test/transfer_cache_spec.ts @@ -42,6 +42,7 @@ interface RequestParams { transferCache?: {includeHeaders: string[]} | boolean; headers?: {[key: string]: string}; withCredentials?: boolean; + credentials?: RequestCredentials; body?: RequestBody; } @@ -441,6 +442,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'}); @@ -603,6 +634,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'});