Repository: authts/oidc-client-ts
Pull request #778 (2022-11-23 08:47:32)

Make it possible to include credentials on all requests.
When using a proxy or cdn like AWS cloudfront you might need to pass cookies when doing http requests from your web application to your backend service. Ex if your infrastructure requires a token cookie to pass requests through to the backend endpoints.

Right now the oidc-client-ts library doesn't support using credentials on all fetch calls. Because of this the library doesn't support the above use case.

I looked at this pull request: [https://github.com/authts/oidc-client-ts/pull/659](https://github.com/authts/oidc-client-ts/pull/659) which adds support of credentials for the refreshToken fetch request. But it is only supports refreshToken.

In my pull request I changed this to be a fetchRequestCredentials which will put credentials on all fetch calls within the library because in the above use case credentials in needed in all fetch requests.

I could also add support credentials for each fetch request like the refreshToken so there would be settings like this:
- userInfoCredentials
- refreshTokenCredentials
- tokenCredentials
- openConfigurationCredentials
- etc

What is your feedback on this?

### Checklist

- [x] This PR makes changes to the public API <!-- was the API report (docs/oidc-client-ts.api.md) updated by this PR? -->
- [x] I have included links for closing relevant issue numbers


pamapa: thanks for contributing, i will merge when all issues are resolved...
codecov[bot]: # [Codecov](https://codecov.io/gh/authts/oidc-client-ts/pull/778?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=authts) Report
Base: **77.65**% // Head: **77.71**% // Increases project coverage by **`+0.05%`** :tada:
> Coverage data is based on head [(`f2b8a0a`)](https://codecov.io/gh/authts/oidc-client-ts/pull/778?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=authts) compared to base [(`c52ba8b`)](https://codecov.io/gh/authts/oidc-client-ts/commit/c52ba8beea0038cae04d07caac62eb1ce29d6329?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=authts).
> Patch coverage: 100.00% of modified lines in pull request are covered.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #778      +/-   ##
==========================================
+ Coverage   77.65%   77.71%   +0.05%     
==========================================
  Files          44       44              
  Lines        1665     1669       +4     
  Branches      319      320       +1     
==========================================
+ Hits         1293     1297       +4     
  Misses        339      339              
  Partials       33       33              
```

| Flag | Coverage Δ | |
|---|---|---|
| unittests | `77.71% <100.00%> (+0.05%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=authts#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/authts/oidc-client-ts/pull/778?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=authts) | Coverage Δ | |
|---|---|---|
| [src/OidcClient.ts](https://codecov.io/gh/authts/oidc-client-ts/pull/778/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=authts#diff-c3JjL09pZGNDbGllbnQudHM=) | `93.75% <ø> (ø)` | |
| [src/JsonService.ts](https://codecov.io/gh/authts/oidc-client-ts/pull/778/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=authts#diff-c3JjL0pzb25TZXJ2aWNlLnRz) | `75.64% <100.00%> (ø)` | |
| [src/MetadataService.ts](https://codecov.io/gh/authts/oidc-client-ts/pull/778/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=authts#diff-c3JjL01ldGFkYXRhU2VydmljZS50cw==) | `95.38% <100.00%> (+0.22%)` | :arrow_up: |
| [src/OidcClientSettings.ts](https://codecov.io/gh/authts/oidc-client-ts/pull/778/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=authts#diff-c3JjL09pZGNDbGllbnRTZXR0aW5ncy50cw==) | `98.33% <100.00%> (ø)` | |
| [src/ResponseValidator.ts](https://codecov.io/gh/authts/oidc-client-ts/pull/778/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=authts#diff-c3JjL1Jlc3BvbnNlVmFsaWRhdG9yLnRz) | `90.24% <100.00%> (ø)` | |
| [src/TokenClient.ts](https://codecov.io/gh/authts/oidc-client-ts/pull/778/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=authts#diff-c3JjL1Rva2VuQ2xpZW50LnRz) | `96.11% <100.00%> (ø)` | |
| [src/UserInfoService.ts](https://codecov.io/gh/authts/oidc-client-ts/pull/778/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=authts#diff-c3JjL1VzZXJJbmZvU2VydmljZS50cw==) | `69.56% <100.00%> (+1.38%)` | :arrow_up: |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=authts). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=authts)

</details>

[:umbrella: View full report at Codecov](https://codecov.io/gh/authts/oidc-client-ts/pull/778?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=authts).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=authts).

Sojborg: Thank you for fast replies. Pushed the fixes. Just have a minor question around refreshTokenCredentials.

Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index bbdcbdeb..a432cc66 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -10,14 +10,14 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-      - uses: actions/checkout@v2
-      - uses: actions/setup-node@v2
+      - uses: actions/checkout@v3
+      - uses: actions/setup-node@v3
         with:
           node-version: 16.x
           cache: npm
 
       - name: Cache incremental builds
-        uses: actions/cache@v2
+        uses: actions/cache@v3
         with:
           path: |
             lib/
@@ -41,14 +41,14 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-      - uses: actions/checkout@v2
-      - uses: actions/setup-node@v2
+      - uses: actions/checkout@v3
+      - uses: actions/setup-node@v3
         with:
           node-version: 16.x
           cache: npm
 
       - name: Cache incremental builds
-        uses: actions/cache@v2
+        uses: actions/cache@v3
         with:
           path: |
             .eslintcache
@@ -62,7 +62,7 @@ jobs:
       - run: npm run lint
       - run: npm test
 
-      - uses: codecov/codecov-action@v2
+      - uses: codecov/codecov-action@v3
         with:
           files: ./coverage/lcov.info
           flags: unittests
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 97db7b4f..dc977ce8 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -8,9 +8,9 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-      - uses: actions/checkout@v2
+      - uses: actions/checkout@v3
       # Setup .npmrc file to publish to npm
-      - uses: actions/setup-node@v2
+      - uses: actions/setup-node@v3
         with:
           node-version: 16.x
           cache: npm
diff --git a/docs/oidc-client-ts.api.md b/docs/oidc-client-ts.api.md
index 6d07989f..9551d2da 100644
--- a/docs/oidc-client-ts.api.md
+++ b/docs/oidc-client-ts.api.md
@@ -343,6 +343,7 @@ export interface OidcClientSettings {
     extraQueryParams?: Record<string, string | number | boolean>;
     // (undocumented)
     extraTokenParams?: Record<string, unknown>;
+    fetchRequestCredentials?: RequestCredentials;
     filterProtocolClaims?: boolean;
     loadUserInfo?: boolean;
     max_age?: number;
@@ -354,6 +355,7 @@ export interface OidcClientSettings {
     post_logout_redirect_uri?: string;
     prompt?: string;
     redirect_uri: string;
+    // @deprecated (undocumented)
     refreshTokenCredentials?: "same-origin" | "include" | "omit";
     resource?: string;
     response_mode?: "query" | "fragment";
@@ -370,7 +372,7 @@ export interface OidcClientSettings {
 
 // @public
 export class OidcClientSettingsStore {
-    constructor({ authority, metadataUrl, metadata, signingKeys, metadataSeed, client_id, client_secret, response_type, scope, redirect_uri, post_logout_redirect_uri, client_authentication, prompt, display, max_age, ui_locales, acr_values, resource, response_mode, filterProtocolClaims, loadUserInfo, staleStateAgeInSeconds, clockSkewInSeconds, userInfoJwtIssuer, mergeClaims, stateStore, refreshTokenCredentials, revokeTokenAdditionalContentTypes, extraQueryParams, extraTokenParams, }: OidcClientSettings);
+    constructor({ authority, metadataUrl, metadata, signingKeys, metadataSeed, client_id, client_secret, response_type, scope, redirect_uri, post_logout_redirect_uri, client_authentication, prompt, display, max_age, ui_locales, acr_values, resource, response_mode, filterProtocolClaims, loadUserInfo, staleStateAgeInSeconds, clockSkewInSeconds, userInfoJwtIssuer, mergeClaims, stateStore, refreshTokenCredentials, revokeTokenAdditionalContentTypes, fetchRequestCredentials, extraQueryParams, extraTokenParams, }: OidcClientSettings);
     // (undocumented)
     readonly acr_values: string | undefined;
     // (undocumented)
@@ -390,6 +392,8 @@ export class OidcClientSettingsStore {
     // (undocumented)
     readonly extraTokenParams: Record<string, unknown>;
     // (undocumented)
+    readonly fetchRequestCredentials: RequestCredentials;
+    // (undocumented)
     readonly filterProtocolClaims: boolean;
     // (undocumented)
     readonly loadUserInfo: boolean;
@@ -410,8 +414,6 @@ export class OidcClientSettingsStore {
     // (undocumented)
     readonly redirect_uri: string;
     // (undocumented)
-    readonly refreshTokenCredentials: "same-origin" | "include" | "omit";
-    // (undocumented)
     readonly resource: string | undefined;
     // (undocumented)
     readonly response_mode: "query" | "fragment";
diff --git a/src/JsonService.ts b/src/JsonService.ts
index 95ff00f8..ce05c9f0 100644
--- a/src/JsonService.ts
+++ b/src/JsonService.ts
@@ -14,6 +14,7 @@ export type JwtHandler = (text: string) => Promise<Record<string, unknown>>;
  */
 export interface GetJsonOpts {
     token?: string;
+    credentials?: RequestCredentials;
 }
 
 /**
@@ -73,6 +74,7 @@ export class JsonService {
 
     public async getJson(url: string, {
         token,
+        credentials,
     }: GetJsonOpts = {}): Promise<Record<string, unknown>> {
         const logger = this._logger.create("getJson");
         const headers: HeadersInit = {
@@ -86,7 +88,7 @@ export class JsonService {
         let response: Response;
         try {
             logger.debug("url:", url);
-            response = await this.fetchWithTimeout(url, { method: "GET", headers });
+            response = await this.fetchWithTimeout(url, { method: "GET", headers, credentials });
         }
         catch (err) {
             logger.error("Network Error");
diff --git a/src/MetadataService.ts b/src/MetadataService.ts
index 8c39177a..2dbe4907 100644
--- a/src/MetadataService.ts
+++ b/src/MetadataService.ts
@@ -17,6 +17,7 @@ export class MetadataService {
     private _metadataUrl: string;
     private _signingKeys: SigningKey[] | null = null;
     private _metadata: Partial<OidcMetadata> | null = null;
+    private _fetchRequestCredentials: RequestCredentials | undefined;
 
     public constructor(private readonly _settings: OidcClientSettingsStore) {
         this._metadataUrl = this._settings.metadataUrl;
@@ -30,6 +31,11 @@ export class MetadataService {
             this._logger.debug("using metadata from settings");
             this._metadata = this._settings.metadata;
         }
+
+        if (this._settings.fetchRequestCredentials) {
+            this._logger.debug("using fetchRequestCredentials from settings");
+            this._fetchRequestCredentials = this._settings.fetchRequestCredentials;
+        }
     }
 
     public resetSigningKeys(): void {
@@ -49,7 +55,7 @@ export class MetadataService {
         }
 
         logger.debug("getting metadata from", this._metadataUrl);
-        const metadata = await this._jsonService.getJson(this._metadataUrl);
+        const metadata = await this._jsonService.getJson(this._metadataUrl, { credentials: this._fetchRequestCredentials });
 
         logger.debug("merging remote JSON with seed metadata");
         this._metadata = Object.assign({}, this._settings.metadataSeed, metadata);
diff --git a/src/OidcClient.ts b/src/OidcClient.ts
index 897e22bd..ac7bd62b 100644
--- a/src/OidcClient.ts
+++ b/src/OidcClient.ts
@@ -196,7 +196,6 @@ export class OidcClient {
             refresh_token: state.refresh_token,
             scope: state.scope,
             timeoutInSeconds,
-            refreshTokenCredentials: this.settings.refreshTokenCredentials,
         });
         const response = new SigninResponse(new URLSearchParams());
         Object.assign(response, result);
diff --git a/src/OidcClientSettings.ts b/src/OidcClientSettings.ts
index 90e17eab..df3773a4 100644
--- a/src/OidcClientSettings.ts
+++ b/src/OidcClientSettings.ts
@@ -104,7 +104,7 @@ export interface OidcClientSettings {
     extraTokenParams?: Record<string, unknown>;
 
     /**
-     * Credentials used by fetch with the refresh request. (default: "same-origin")
+     * @deprecated since version 2.1.0. Use fetchRequestCredentials instead.
      */
     refreshTokenCredentials?: "same-origin" | "include" | "omit";
 
@@ -112,6 +112,12 @@ export interface OidcClientSettings {
      * Will check the content type header of the response of the revocation endpoint to match these passed values (default: [])
      */
     revokeTokenAdditionalContentTypes?: string[];
+
+    /**
+     * Sets the credentials for fetch requests. (default: "same-origin")
+     * Use this if you need to send cookies to the OIDC/OAuth2 provider or if you are using a proxy that requires cookies
+     */
+    fetchRequestCredentials?: RequestCredentials;
 }
 
 /**
@@ -160,8 +166,8 @@ export class OidcClientSettingsStore {
     public readonly extraQueryParams: Record<string, string | number | boolean>;
     public readonly extraTokenParams: Record<string, unknown>;
 
-    public readonly refreshTokenCredentials: "same-origin" | "include" | "omit";
     public readonly revokeTokenAdditionalContentTypes?: string[];
+    public readonly fetchRequestCredentials: RequestCredentials;
 
     public constructor({
         // metadata related
@@ -181,8 +187,9 @@ export class OidcClientSettingsStore {
         mergeClaims = false,
         // other behavior
         stateStore,
-        refreshTokenCredentials = "same-origin",
+        refreshTokenCredentials,
         revokeTokenAdditionalContentTypes,
+        fetchRequestCredentials,
         // extra query params
         extraQueryParams = {},
         extraTokenParams = {},
@@ -229,9 +236,14 @@ export class OidcClientSettingsStore {
         this.userInfoJwtIssuer = userInfoJwtIssuer;
         this.mergeClaims = !!mergeClaims;
 
-        this.refreshTokenCredentials = refreshTokenCredentials;
         this.revokeTokenAdditionalContentTypes = revokeTokenAdditionalContentTypes;
 
+        if (fetchRequestCredentials && refreshTokenCredentials) {
+            console.warn("Both fetchRequestCredentials and refreshTokenCredentials is set. Only fetchRequestCredentials will be used.");
+        }
+        this.fetchRequestCredentials = fetchRequestCredentials ? fetchRequestCredentials
+            : refreshTokenCredentials ? refreshTokenCredentials : "same-origin";
+
         if (stateStore) {
             this.stateStore = stateStore;
         }
diff --git a/src/ResponseValidator.ts b/src/ResponseValidator.ts
index 4ebd16cb..e811472e 100644
--- a/src/ResponseValidator.ts
+++ b/src/ResponseValidator.ts
@@ -45,7 +45,7 @@ const ProtocolClaims = [
  */
 export class ResponseValidator {
     protected readonly _logger = new Logger("ResponseValidator");
-    protected readonly _userInfoService = new UserInfoService(this._metadataService);
+    protected readonly _userInfoService = new UserInfoService(this._settings, this._metadataService);
     protected readonly _tokenClient = new TokenClient(this._settings, this._metadataService);
 
     public constructor(
diff --git a/src/TokenClient.ts b/src/TokenClient.ts
index 74549548..3678fcfc 100644
--- a/src/TokenClient.ts
+++ b/src/TokenClient.ts
@@ -45,7 +45,6 @@ export interface ExchangeRefreshTokenArgs {
     scope?: string;
 
     timeoutInSeconds?: number;
-    refreshTokenCredentials?: "same-origin" | "include" | "omit";
 }
 
 /**
@@ -117,7 +116,7 @@ export class TokenClient {
         const url = await this._metadataService.getTokenEndpoint(false);
         logger.debug("got token endpoint");
 
-        const response = await this._jsonService.postForm(url, { body: params, basicAuth });
+        const response = await this._jsonService.postForm(url, { body: params, basicAuth, initCredentials: this._settings.fetchRequestCredentials });
         logger.debug("got response");
 
         return response;
@@ -159,7 +158,7 @@ export class TokenClient {
         const url = await this._metadataService.getTokenEndpoint(false);
         logger.debug("got token endpoint");
 
-        const response = await this._jsonService.postForm(url, { body: params, basicAuth });
+        const response = await this._jsonService.postForm(url, { body: params, basicAuth, initCredentials: this._settings.fetchRequestCredentials });
         logger.debug("got response");
 
         return response;
@@ -170,7 +169,6 @@ export class TokenClient {
         client_id = this._settings.client_id,
         client_secret = this._settings.client_secret,
         timeoutInSeconds,
-        refreshTokenCredentials,
         ...args
     }: ExchangeRefreshTokenArgs): Promise<Record<string, unknown>> {
         const logger = this._logger.create("exchangeRefreshToken");
@@ -207,7 +205,7 @@ export class TokenClient {
         const url = await this._metadataService.getTokenEndpoint(false);
         logger.debug("got token endpoint");
 
-        const response = await this._jsonService.postForm(url, { body: params, basicAuth, timeoutInSeconds, initCredentials: refreshTokenCredentials });
+        const response = await this._jsonService.postForm(url, { body: params, basicAuth, timeoutInSeconds, initCredentials: this._settings.fetchRequestCredentials });
         logger.debug("got response");
 
         return response;
diff --git a/src/UserInfoService.ts b/src/UserInfoService.ts
index fe7803ed..20a536c7 100644
--- a/src/UserInfoService.ts
+++ b/src/UserInfoService.ts
@@ -5,6 +5,7 @@ import { Logger, JwtUtils } from "./utils";
 import { JsonService } from "./JsonService";
 import type { MetadataService } from "./MetadataService";
 import type { JwtClaims } from "./Claims";
+import type { OidcClientSettingsStore } from "./OidcClientSettings";
 
 /**
  * @internal
@@ -13,7 +14,9 @@ export class UserInfoService {
     protected readonly _logger = new Logger("UserInfoService");
     private readonly _jsonService: JsonService;
 
-    public constructor(private readonly _metadataService: MetadataService) {
+    public constructor(private readonly _settings: OidcClientSettingsStore,
+        private readonly _metadataService: MetadataService,
+    ) {
         this._jsonService = new JsonService(undefined, this._getClaimsFromJwt);
     }
 
@@ -26,7 +29,10 @@ export class UserInfoService {
         const url = await this._metadataService.getUserInfoEndpoint();
         logger.debug("got userinfo url", url);
 
-        const claims = await this._jsonService.getJson(url, { token });
+        const claims = await this._jsonService.getJson(url, {
+            token,
+            credentials: this._settings.fetchRequestCredentials,
+        });
         logger.debug("got claims", claims);
 
         return claims;
@@ -39,8 +45,7 @@ export class UserInfoService {
             logger.debug("JWT decoding successful");
 
             return payload;
-        }
-        catch (err) {
+        } catch (err) {
             logger.error("Error parsing JWT response");
             throw err;
         }


Test patch:
diff --git a/src/MetadataService.test.ts b/src/MetadataService.test.ts
index 1180ace7..471528f2 100644
--- a/src/MetadataService.test.ts
+++ b/src/MetadataService.test.ts
@@ -58,7 +58,7 @@ describe("MetadataService", () => {
             await subject.getMetadata();
 
             // assert
-            expect(getJsonMock).toBeCalledWith("authority/.well-known/openid-configuration");
+            expect(getJsonMock).toBeCalledWith("authority/.well-known/openid-configuration", { credentials: "same-origin" });
         });
 
         it("should fail when no authority or metadataUrl configured", async () => {
@@ -93,7 +93,7 @@ describe("MetadataService", () => {
             await subject.getMetadata();
 
             // assert
-            expect(getJsonMock).toBeCalledWith("http://sts/metadata");
+            expect(getJsonMock).toBeCalledWith("http://sts/metadata", { credentials: "same-origin" });
         });
 
         it("should return metadata from json call", async () => {
@@ -177,6 +177,27 @@ describe("MetadataService", () => {
                 // assert
                 .rejects.toThrow(error);
         });
+
+        it("should use getRequestCredentials to make json call when set", async () => {
+            // arrange
+            settings = {
+                authority: "authority",
+                client_id: "client",
+                redirect_uri: "redirect",
+                metadataUrl: "http://sts/metadata",
+                fetchRequestCredentials: "include",
+            };
+            subject = new MetadataService(new OidcClientSettingsStore(settings));
+            const jsonService = subject["_jsonService"]; // access private member
+            const getJsonMock = jest.spyOn(jsonService, "getJson")
+                .mockResolvedValue({ foo: "bar" });
+
+            // act
+            await subject.getMetadata();
+
+            // assert
+            expect(getJsonMock).toBeCalledWith("http://sts/metadata", { credentials: "include" });
+        });
     });
 
     describe("getIssuer", () => {
diff --git a/src/OidcClient.test.ts b/src/OidcClient.test.ts
index d2798b05..35ca2a0b 100644
--- a/src/OidcClient.test.ts
+++ b/src/OidcClient.test.ts
@@ -432,7 +432,6 @@ describe("OidcClient", () => {
                 refresh_token: "refresh_token",
                 scope: "openid",
                 timeoutInSeconds: undefined,
-                refreshTokenCredentials: "same-origin",
             });
             expect(response).toBeInstanceOf(SigninResponse);
             expect(response).toMatchObject(tokenResponse);
diff --git a/src/UserInfoService.test.ts b/src/UserInfoService.test.ts
index 060a9c54..f5767503 100644
--- a/src/UserInfoService.test.ts
+++ b/src/UserInfoService.test.ts
@@ -16,10 +16,11 @@ describe("UserInfoService", () => {
             authority: "authority",
             client_id: "client",
             redirect_uri: "redirect",
+            fetchRequestCredentials: "include",
         });
         metadataService = new MetadataService(settings);
 
-        subject = new UserInfoService(metadataService);
+        subject = new UserInfoService(settings, metadataService);
 
         // access private members
         jsonService = subject["_jsonService"];
@@ -101,5 +102,22 @@ describe("UserInfoService", () => {
             // assert
             expect(claims).toEqual(expectedClaims);
         });
+
+        it("should use settings fetchRequestCredentials to set credentials on user info request", async () => {
+            // arrange
+            jest.spyOn(metadataService, "getUserInfoEndpoint").mockImplementation(() => Promise.resolve("http://sts/userinfo"));
+            const getJsonMock = jest.spyOn(jsonService, "getJson").mockImplementation(() => Promise.resolve({}));
+
+            // act
+            await subject.getClaims("token");
+
+            // assert
+            expect(getJsonMock).toBeCalledWith(
+                "http://sts/userinfo",
+                expect.objectContaining({
+                    credentials: "include",
+                }),
+            );
+        });
     });
 });