Repository: authts/oidc-client-ts
Pull request #659 (2022-08-30 09:00:12)

new option refreshWithCredentials sends cookies with token refresh
fixes https://github.com/authts/oidc-client-ts/issues/607

PR could be improved by allowing more `credentials` settings besides include.

the PR is very helpful in the case that the openid server sets cookies which can be updated by refresh so that silent signin works longer.

### 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: - Test fails
- would be nice if as you noted more options would be supported
  - "same-origin" (default)
  - "include"
  - "omit"
- the settings name could be more explicit
  - refreshTokenCredentials?: "same-origin" | "include" | "omit"

oberhamsi: thanks for the quick review, i fixed the unit test and changed it to `refreshTokenCredentials` and allow all possible options
codecov[bot]: # [Codecov](https://codecov.io/gh/authts/oidc-client-ts/pull/659?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=authts) Report
> Merging [#659](https://codecov.io/gh/authts/oidc-client-ts/pull/659?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=authts) (18cc628) into [main](https://codecov.io/gh/authts/oidc-client-ts/commit/2bed3c95a6c133e9ce3843e3f8df685c895ad4ab?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=authts) (2bed3c9) will **decrease** coverage by `0.06%`.
> The diff coverage is `66.66%`.

```diff
@@            Coverage Diff             @@
##             main     #659      +/-   ##
==========================================
- Coverage   76.91%   76.85%   -0.07%     
==========================================
  Files          44       44              
  Lines        1590     1594       +4     
  Branches      299      300       +1     
==========================================
+ Hits         1223     1225       +2     
- Misses        336      338       +2     
  Partials       31       31              
```

| Flag | Coverage Δ | |
|---|---|---|
| unittests | `76.85% <66.66%> (-0.07%)` | :arrow_down: |

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/659?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/659/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=authts#diff-c3JjL09pZGNDbGllbnQudHM=) | `94.33% <ø> (ø)` | |
| [src/JsonService.ts](https://codecov.io/gh/authts/oidc-client-ts/pull/659/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=authts#diff-c3JjL0pzb25TZXJ2aWNlLnRz) | `74.07% <50.00%> (-1.57%)` | :arrow_down: |
| [src/OidcClientSettings.ts](https://codecov.io/gh/authts/oidc-client-ts/pull/659/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=authts#diff-c3JjL09pZGNDbGllbnRTZXR0aW5ncy50cw==) | `98.27% <100.00%> (+0.03%)` | :arrow_up: |
| [src/TokenClient.ts](https://codecov.io/gh/authts/oidc-client-ts/pull/659/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=authts#diff-c3JjL1Rva2VuQ2xpZW50LnRz) | `96.25% <100.00%> (ø)` | |

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)

pamapa: Looks good, ready to merge? I will squash all commit into one unless you rebase first :-) ...
oberhamsi: Yes, looks good to me. Merge at your pleasure 

Patch:
diff --git a/docs/oidc-client-ts.api.md b/docs/oidc-client-ts.api.md
index c92188dc..10fb1c19 100644
--- a/docs/oidc-client-ts.api.md
+++ b/docs/oidc-client-ts.api.md
@@ -342,6 +342,7 @@ export interface OidcClientSettings {
     post_logout_redirect_uri?: string;
     prompt?: string;
     redirect_uri: string;
+    refreshTokenCredentials?: "same-origin" | "include" | "omit";
     resource?: string;
     response_mode?: "query" | "fragment";
     response_type?: string;
@@ -356,7 +357,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, 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, extraQueryParams, extraTokenParams, }: OidcClientSettings);
     // (undocumented)
     readonly acr_values: string | undefined;
     // (undocumented)
@@ -396,6 +397,8 @@ 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 74e754bd..95ff00f8 100644
--- a/src/JsonService.ts
+++ b/src/JsonService.ts
@@ -23,6 +23,7 @@ export interface PostFormOpts {
     body: URLSearchParams;
     basicAuth?: string;
     timeoutInSeconds?: number;
+    initCredentials?: "same-origin" | "include" | "omit";
 }
 
 /**
@@ -123,6 +124,7 @@ export class JsonService {
         body,
         basicAuth,
         timeoutInSeconds,
+        initCredentials,
     }: PostFormOpts): Promise<Record<string, unknown>> {
         const logger = this._logger.create("postForm");
         const headers: HeadersInit = {
@@ -136,7 +138,7 @@ export class JsonService {
         let response: Response;
         try {
             logger.debug("url:", url);
-            response = await this.fetchWithTimeout(url, { method: "POST", headers, body, timeoutInSeconds });
+            response = await this.fetchWithTimeout(url, { method: "POST", headers, body, timeoutInSeconds, credentials: initCredentials });
         }
         catch (err) {
             logger.error("Network error");
diff --git a/src/OidcClient.ts b/src/OidcClient.ts
index fb2c4319..f367f585 100644
--- a/src/OidcClient.ts
+++ b/src/OidcClient.ts
@@ -175,6 +175,7 @@ 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 3512a9ac..e82be61a 100644
--- a/src/OidcClientSettings.ts
+++ b/src/OidcClientSettings.ts
@@ -102,6 +102,11 @@ export interface OidcClientSettings {
     extraQueryParams?: Record<string, string | number | boolean>;
 
     extraTokenParams?: Record<string, unknown>;
+
+    /**
+     * Credentials used by fetch with the refresh request. (default: "same-origin")
+     */
+    refreshTokenCredentials?: "same-origin" | "include" | "omit";
 }
 
 /**
@@ -150,6 +155,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 constructor({
         // metadata related
         authority, metadataUrl, metadata, signingKeys, metadataSeed,
@@ -168,6 +175,7 @@ export class OidcClientSettingsStore {
         mergeClaims = false,
         // other behavior
         stateStore,
+        refreshTokenCredentials = "same-origin",
         // extra query params
         extraQueryParams = {},
         extraTokenParams = {},
@@ -214,6 +222,8 @@ export class OidcClientSettingsStore {
         this.userInfoJwtIssuer = userInfoJwtIssuer;
         this.mergeClaims = !!mergeClaims;
 
+        this.refreshTokenCredentials = refreshTokenCredentials;
+
         if (stateStore) {
             this.stateStore = stateStore;
         }
diff --git a/src/TokenClient.ts b/src/TokenClient.ts
index b0d65827..a7988b27 100644
--- a/src/TokenClient.ts
+++ b/src/TokenClient.ts
@@ -31,6 +31,7 @@ export interface ExchangeRefreshTokenArgs {
     scope?: string;
 
     timeoutInSeconds?: number;
+    refreshTokenCredentials?: "same-origin" | "include" | "omit";
 }
 
 /**
@@ -111,6 +112,7 @@ 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");
@@ -147,7 +149,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 });
+        const response = await this._jsonService.postForm(url, { body: params, basicAuth, timeoutInSeconds, initCredentials: refreshTokenCredentials });
         logger.debug("got response");
 
         return response;


Test patch:
diff --git a/src/OidcClient.test.ts b/src/OidcClient.test.ts
index 6d597bd5..af5ab08b 100644
--- a/src/OidcClient.test.ts
+++ b/src/OidcClient.test.ts
@@ -380,6 +380,8 @@ describe("OidcClient", () => {
             expect(exchangeRefreshTokenMock).toHaveBeenCalledWith( {
                 refresh_token: "refresh_token",
                 scope: "openid",
+                timeoutInSeconds: undefined,
+                refreshTokenCredentials: "same-origin",
             });
             expect(response).toBeInstanceOf(SigninResponse);
             expect(response).toMatchObject(tokenResponse);