@emilgroup/migration-sdk 0.10.1-beta.3 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,11 +17,11 @@ Although this package can be used in both TypeScript and JavaScript, it is inten
17
17
  Navigate to the folder of your consuming project and run one of the following commands:
18
18
 
19
19
  ```
20
- npm install @emilgroup/migration-sdk@0.10.1-beta.3 --save
20
+ npm install @emilgroup/migration-sdk@0.11.0 --save
21
21
  ```
22
22
  or
23
23
  ```
24
- yarn add @emilgroup/migration-sdk@0.10.1-beta.3
24
+ yarn add @emilgroup/migration-sdk@0.11.0
25
25
  ```
26
26
 
27
27
  And then you can import ``.
@@ -101,8 +101,10 @@ export const MigrationApiAxiosParamCreator = function (configuration?: Configura
101
101
  // http bearer authentication required
102
102
  await setBearerAuthToObject(localVarHeaderParameter, configuration)
103
103
 
104
- if (authorization !== undefined && authorization !== null || baseAccessToken !== undefined && baseAccessToken !== null) {
105
- localVarHeaderParameter['Authorization'] = String(authorization ? authorization : baseAccessToken);
104
+ // Only Authorization falls back to the configured token; an empty value is never sent.
105
+ const authorizationValue = 'Authorization'.toLowerCase() === 'authorization' ? (authorization || baseAccessToken) : authorization;
106
+ if (authorizationValue) {
107
+ localVarHeaderParameter['Authorization'] = String(authorizationValue);
106
108
  }
107
109
 
108
110
 
@@ -152,8 +154,10 @@ export const MigrationApiAxiosParamCreator = function (configuration?: Configura
152
154
  // http bearer authentication required
153
155
  await setBearerAuthToObject(localVarHeaderParameter, configuration)
154
156
 
155
- if (authorization !== undefined && authorization !== null || baseAccessToken !== undefined && baseAccessToken !== null) {
156
- localVarHeaderParameter['Authorization'] = String(authorization ? authorization : baseAccessToken);
157
+ // Only Authorization falls back to the configured token; an empty value is never sent.
158
+ const authorizationValue = 'Authorization'.toLowerCase() === 'authorization' ? (authorization || baseAccessToken) : authorization;
159
+ if (authorizationValue) {
160
+ localVarHeaderParameter['Authorization'] = String(authorizationValue);
157
161
  }
158
162
 
159
163
 
@@ -199,8 +203,10 @@ export const MigrationApiAxiosParamCreator = function (configuration?: Configura
199
203
  // http bearer authentication required
200
204
  await setBearerAuthToObject(localVarHeaderParameter, configuration)
201
205
 
202
- if (authorization !== undefined && authorization !== null || baseAccessToken !== undefined && baseAccessToken !== null) {
203
- localVarHeaderParameter['Authorization'] = String(authorization ? authorization : baseAccessToken);
206
+ // Only Authorization falls back to the configured token; an empty value is never sent.
207
+ const authorizationValue = 'Authorization'.toLowerCase() === 'authorization' ? (authorization || baseAccessToken) : authorization;
208
+ if (authorizationValue) {
209
+ localVarHeaderParameter['Authorization'] = String(authorizationValue);
204
210
  }
205
211
 
206
212
 
@@ -246,8 +252,10 @@ export const MigrationApiAxiosParamCreator = function (configuration?: Configura
246
252
  // http bearer authentication required
247
253
  await setBearerAuthToObject(localVarHeaderParameter, configuration)
248
254
 
249
- if (authorization !== undefined && authorization !== null || baseAccessToken !== undefined && baseAccessToken !== null) {
250
- localVarHeaderParameter['Authorization'] = String(authorization ? authorization : baseAccessToken);
255
+ // Only Authorization falls back to the configured token; an empty value is never sent.
256
+ const authorizationValue = 'Authorization'.toLowerCase() === 'authorization' ? (authorization || baseAccessToken) : authorization;
257
+ if (authorizationValue) {
258
+ localVarHeaderParameter['Authorization'] = String(authorizationValue);
251
259
  }
252
260
 
253
261
 
@@ -293,8 +301,10 @@ export const MigrationApiAxiosParamCreator = function (configuration?: Configura
293
301
  // http bearer authentication required
294
302
  await setBearerAuthToObject(localVarHeaderParameter, configuration)
295
303
 
296
- if (authorization !== undefined && authorization !== null || baseAccessToken !== undefined && baseAccessToken !== null) {
297
- localVarHeaderParameter['Authorization'] = String(authorization ? authorization : baseAccessToken);
304
+ // Only Authorization falls back to the configured token; an empty value is never sent.
305
+ const authorizationValue = 'Authorization'.toLowerCase() === 'authorization' ? (authorization || baseAccessToken) : authorization;
306
+ if (authorizationValue) {
307
+ localVarHeaderParameter['Authorization'] = String(authorizationValue);
298
308
  }
299
309
 
300
310
 
@@ -340,8 +350,10 @@ export const MigrationApiAxiosParamCreator = function (configuration?: Configura
340
350
  // http bearer authentication required
341
351
  await setBearerAuthToObject(localVarHeaderParameter, configuration)
342
352
 
343
- if (authorization !== undefined && authorization !== null || baseAccessToken !== undefined && baseAccessToken !== null) {
344
- localVarHeaderParameter['Authorization'] = String(authorization ? authorization : baseAccessToken);
353
+ // Only Authorization falls back to the configured token; an empty value is never sent.
354
+ const authorizationValue = 'Authorization'.toLowerCase() === 'authorization' ? (authorization || baseAccessToken) : authorization;
355
+ if (authorizationValue) {
356
+ localVarHeaderParameter['Authorization'] = String(authorizationValue);
345
357
  }
346
358
 
347
359
 
@@ -388,8 +400,10 @@ export const MigrationApiAxiosParamCreator = function (configuration?: Configura
388
400
  // http bearer authentication required
389
401
  await setBearerAuthToObject(localVarHeaderParameter, configuration)
390
402
 
391
- if (authorization !== undefined && authorization !== null || baseAccessToken !== undefined && baseAccessToken !== null) {
392
- localVarHeaderParameter['Authorization'] = String(authorization ? authorization : baseAccessToken);
403
+ // Only Authorization falls back to the configured token; an empty value is never sent.
404
+ const authorizationValue = 'Authorization'.toLowerCase() === 'authorization' ? (authorization || baseAccessToken) : authorization;
405
+ if (authorizationValue) {
406
+ localVarHeaderParameter['Authorization'] = String(authorizationValue);
393
407
  }
394
408
 
395
409
 
@@ -433,8 +447,10 @@ export const MigrationApiAxiosParamCreator = function (configuration?: Configura
433
447
  // http bearer authentication required
434
448
  await setBearerAuthToObject(localVarHeaderParameter, configuration)
435
449
 
436
- if (authorization !== undefined && authorization !== null || baseAccessToken !== undefined && baseAccessToken !== null) {
437
- localVarHeaderParameter['Authorization'] = String(authorization ? authorization : baseAccessToken);
450
+ // Only Authorization falls back to the configured token; an empty value is never sent.
451
+ const authorizationValue = 'Authorization'.toLowerCase() === 'authorization' ? (authorization || baseAccessToken) : authorization;
452
+ if (authorizationValue) {
453
+ localVarHeaderParameter['Authorization'] = String(authorizationValue);
438
454
  }
439
455
 
440
456
 
@@ -478,8 +494,10 @@ export const MigrationApiAxiosParamCreator = function (configuration?: Configura
478
494
  // http bearer authentication required
479
495
  await setBearerAuthToObject(localVarHeaderParameter, configuration)
480
496
 
481
- if (authorization !== undefined && authorization !== null || baseAccessToken !== undefined && baseAccessToken !== null) {
482
- localVarHeaderParameter['Authorization'] = String(authorization ? authorization : baseAccessToken);
497
+ // Only Authorization falls back to the configured token; an empty value is never sent.
498
+ const authorizationValue = 'Authorization'.toLowerCase() === 'authorization' ? (authorization || baseAccessToken) : authorization;
499
+ if (authorizationValue) {
500
+ localVarHeaderParameter['Authorization'] = String(authorizationValue);
483
501
  }
484
502
 
485
503
 
@@ -554,8 +572,10 @@ export const MigrationApiAxiosParamCreator = function (configuration?: Configura
554
572
  localVarQueryParameter['filters'] = filters;
555
573
  }
556
574
 
557
- if (authorization !== undefined && authorization !== null || baseAccessToken !== undefined && baseAccessToken !== null) {
558
- localVarHeaderParameter['Authorization'] = String(authorization ? authorization : baseAccessToken);
575
+ // Only Authorization falls back to the configured token; an empty value is never sent.
576
+ const authorizationValue = 'Authorization'.toLowerCase() === 'authorization' ? (authorization || baseAccessToken) : authorization;
577
+ if (authorizationValue) {
578
+ localVarHeaderParameter['Authorization'] = String(authorizationValue);
559
579
  }
560
580
 
561
581
 
@@ -630,8 +650,10 @@ export const MigrationApiAxiosParamCreator = function (configuration?: Configura
630
650
  localVarQueryParameter['filters'] = filters;
631
651
  }
632
652
 
633
- if (authorization !== undefined && authorization !== null || baseAccessToken !== undefined && baseAccessToken !== null) {
634
- localVarHeaderParameter['Authorization'] = String(authorization ? authorization : baseAccessToken);
653
+ // Only Authorization falls back to the configured token; an empty value is never sent.
654
+ const authorizationValue = 'Authorization'.toLowerCase() === 'authorization' ? (authorization || baseAccessToken) : authorization;
655
+ if (authorizationValue) {
656
+ localVarHeaderParameter['Authorization'] = String(authorizationValue);
635
657
  }
636
658
 
637
659
 
@@ -710,8 +732,10 @@ export const MigrationApiAxiosParamCreator = function (configuration?: Configura
710
732
  localVarQueryParameter['filters'] = filters;
711
733
  }
712
734
 
713
- if (authorization !== undefined && authorization !== null || baseAccessToken !== undefined && baseAccessToken !== null) {
714
- localVarHeaderParameter['Authorization'] = String(authorization ? authorization : baseAccessToken);
735
+ // Only Authorization falls back to the configured token; an empty value is never sent.
736
+ const authorizationValue = 'Authorization'.toLowerCase() === 'authorization' ? (authorization || baseAccessToken) : authorization;
737
+ if (authorizationValue) {
738
+ localVarHeaderParameter['Authorization'] = String(authorizationValue);
715
739
  }
716
740
 
717
741
 
@@ -790,8 +814,10 @@ export const MigrationApiAxiosParamCreator = function (configuration?: Configura
790
814
  localVarQueryParameter['filters'] = filters;
791
815
  }
792
816
 
793
- if (authorization !== undefined && authorization !== null || baseAccessToken !== undefined && baseAccessToken !== null) {
794
- localVarHeaderParameter['Authorization'] = String(authorization ? authorization : baseAccessToken);
817
+ // Only Authorization falls back to the configured token; an empty value is never sent.
818
+ const authorizationValue = 'Authorization'.toLowerCase() === 'authorization' ? (authorization || baseAccessToken) : authorization;
819
+ if (authorizationValue) {
820
+ localVarHeaderParameter['Authorization'] = String(authorizationValue);
795
821
  }
796
822
 
797
823
 
@@ -866,8 +892,10 @@ export const MigrationApiAxiosParamCreator = function (configuration?: Configura
866
892
  localVarQueryParameter['filters'] = filters;
867
893
  }
868
894
 
869
- if (authorization !== undefined && authorization !== null || baseAccessToken !== undefined && baseAccessToken !== null) {
870
- localVarHeaderParameter['Authorization'] = String(authorization ? authorization : baseAccessToken);
895
+ // Only Authorization falls back to the configured token; an empty value is never sent.
896
+ const authorizationValue = 'Authorization'.toLowerCase() === 'authorization' ? (authorization || baseAccessToken) : authorization;
897
+ if (authorizationValue) {
898
+ localVarHeaderParameter['Authorization'] = String(authorizationValue);
871
899
  }
872
900
 
873
901
 
@@ -914,8 +942,10 @@ export const MigrationApiAxiosParamCreator = function (configuration?: Configura
914
942
  // http bearer authentication required
915
943
  await setBearerAuthToObject(localVarHeaderParameter, configuration)
916
944
 
917
- if (authorization !== undefined && authorization !== null || baseAccessToken !== undefined && baseAccessToken !== null) {
918
- localVarHeaderParameter['Authorization'] = String(authorization ? authorization : baseAccessToken);
945
+ // Only Authorization falls back to the configured token; an empty value is never sent.
946
+ const authorizationValue = 'Authorization'.toLowerCase() === 'authorization' ? (authorization || baseAccessToken) : authorization;
947
+ if (authorizationValue) {
948
+ localVarHeaderParameter['Authorization'] = String(authorizationValue);
919
949
  }
920
950
 
921
951
 
@@ -965,8 +995,10 @@ export const MigrationApiAxiosParamCreator = function (configuration?: Configura
965
995
  // http bearer authentication required
966
996
  await setBearerAuthToObject(localVarHeaderParameter, configuration)
967
997
 
968
- if (authorization !== undefined && authorization !== null || baseAccessToken !== undefined && baseAccessToken !== null) {
969
- localVarHeaderParameter['Authorization'] = String(authorization ? authorization : baseAccessToken);
998
+ // Only Authorization falls back to the configured token; an empty value is never sent.
999
+ const authorizationValue = 'Authorization'.toLowerCase() === 'authorization' ? (authorization || baseAccessToken) : authorization;
1000
+ if (authorizationValue) {
1001
+ localVarHeaderParameter['Authorization'] = String(authorizationValue);
970
1002
  }
971
1003
 
972
1004
 
@@ -1016,8 +1048,10 @@ export const MigrationApiAxiosParamCreator = function (configuration?: Configura
1016
1048
  // http bearer authentication required
1017
1049
  await setBearerAuthToObject(localVarHeaderParameter, configuration)
1018
1050
 
1019
- if (authorization !== undefined && authorization !== null || baseAccessToken !== undefined && baseAccessToken !== null) {
1020
- localVarHeaderParameter['Authorization'] = String(authorization ? authorization : baseAccessToken);
1051
+ // Only Authorization falls back to the configured token; an empty value is never sent.
1052
+ const authorizationValue = 'Authorization'.toLowerCase() === 'authorization' ? (authorization || baseAccessToken) : authorization;
1053
+ if (authorizationValue) {
1054
+ localVarHeaderParameter['Authorization'] = String(authorizationValue);
1021
1055
  }
1022
1056
 
1023
1057
 
package/base.ts CHANGED
@@ -32,9 +32,20 @@ export const COLLECTION_FORMATS = {
32
32
  pipes: "|",
33
33
  };
34
34
 
35
+ export interface SessionClass {
36
+ sub: string;
37
+ username?: string;
38
+ email?: string;
39
+ tenantSlug?: string;
40
+ permissions?: string;
41
+ expiresAt?: number;
42
+ }
43
+
35
44
  export interface LoginClass {
36
45
  accessToken: string;
37
46
  permissions: string;
47
+ authMode?: string;
48
+ session?: SessionClass;
38
49
  }
39
50
 
40
51
 
@@ -68,10 +79,19 @@ interface TokenData {
68
79
  accessToken?: string;
69
80
  username?: string;
70
81
  permissions?: string;
82
+ // 'both' or 'session' when authservice keeps the tokens in a server session.
83
+ authMode?: string;
84
+ sub?: string;
71
85
  }
72
86
 
73
87
  const NETWORK_ERROR_MESSAGE = "Network Error";
74
88
  const TOKEN_DATA = 'APP_TOKEN';
89
+ // Sent only to authservice, to ask for a server session.
90
+ const AUTH_MODE_HEADER = 'X-Emil-Auth-Mode';
91
+ // Names the account on every call; one browser session can hold several.
92
+ const ACCOUNT_HEADER = 'X-Emil-Account';
93
+ const SESSION_MODES = ['both', 'session'];
94
+ const RESEND_SAFE_METHODS = ['get', 'head', 'options'];
75
95
 
76
96
  /**
77
97
  *
@@ -93,10 +113,18 @@ export class BaseAPI {
93
113
  this.configuration = configuration;
94
114
  this.basePath = configuration.basePath || this.basePath;
95
115
 
96
- // Use config token if provided, otherwise use tokenData token
97
- const configToken = this.configuration.accessToken;
98
- const storedToken = accessToken ? `Bearer ${accessToken}` : '';
99
- this.configuration.accessToken = configToken || storedToken;
116
+ if (configuration.cookieAuth) {
117
+ this.configuration.username = this.configuration.username || this.tokenData.username;
118
+ }
119
+
120
+ if (this.usesSession()) {
121
+ this.useSessionAuth();
122
+ } else {
123
+ // Use config token if provided, otherwise use tokenData token
124
+ const configToken = this.configuration.accessToken;
125
+ const storedToken = accessToken ? `Bearer ${accessToken}` : '';
126
+ this.configuration.accessToken = configToken || storedToken;
127
+ }
100
128
  } else {
101
129
  const { accessToken, username } = this.tokenData;
102
130
 
@@ -110,6 +138,107 @@ export class BaseAPI {
110
138
  this.attachInterceptor(axios);
111
139
  }
112
140
 
141
+ // The app's own account wins over APP_TOKEN, which holds whichever tab wrote last.
142
+ private sessionAccount(): string | undefined {
143
+ return this.configuration.account || this.tokenData.sub;
144
+ }
145
+
146
+ // Apps may pass the address as the second argument only; authservice is on the same host.
147
+ private authBasePath(): string {
148
+ return this.configuration.basePath || this.basePath;
149
+ }
150
+
151
+ private usesSession(): boolean {
152
+ return !!this.configuration.cookieAuth && SESSION_MODES.indexOf(this.tokenData.authMode) !== -1 && !!this.sessionAccount();
153
+ }
154
+
155
+ private useSessionAuth() {
156
+ const { headers, ...baseOptions } = this.configuration.baseOptions || {};
157
+ this.configuration.accessToken = '';
158
+ this.configuration.baseOptions = {
159
+ ...baseOptions,
160
+ withCredentials: true,
161
+ headers: { ...headers, [ACCOUNT_HEADER]: this.sessionAccount() },
162
+ };
163
+ }
164
+
165
+ private useHeaderAuth(accessToken: string) {
166
+ this.configuration.accessToken = accessToken ? `Bearer ${accessToken}` : '';
167
+ this.tokenData.accessToken = accessToken;
168
+ delete this.tokenData.authMode;
169
+ delete this.tokenData.sub;
170
+ if (this.configuration.cookieAuth) {
171
+ const { withCredentials, headers, ...baseOptions } = this.configuration.baseOptions || {};
172
+ const { [ACCOUNT_HEADER]: account, ...otherHeaders } = headers || {};
173
+ this.configuration.baseOptions = { ...baseOptions, headers: otherHeaders };
174
+ }
175
+ }
176
+
177
+ private applyTokenReply({ accessToken, authMode, session }: LoginClass) {
178
+ if (this.configuration.cookieAuth && SESSION_MODES.indexOf(authMode) !== -1 && session?.sub) {
179
+ this.tokenData.authMode = authMode;
180
+ this.tokenData.sub = session.sub;
181
+ if (this.configuration.account) {
182
+ // A workspace switch moves the app to another account.
183
+ this.configuration.account = session.sub;
184
+ }
185
+ // Both keeps the token for apps not moved yet; session mode keeps no token at all.
186
+ if (authMode === 'both' && accessToken) {
187
+ this.tokenData.accessToken = accessToken;
188
+ } else {
189
+ delete this.tokenData.accessToken;
190
+ }
191
+ this.useSessionAuth();
192
+ } else {
193
+ this.useHeaderAuth(accessToken);
194
+ }
195
+ }
196
+
197
+ private sessionHeaders(): Record<string, string> {
198
+ if (!this.configuration.cookieAuth) {
199
+ return {};
200
+ }
201
+ return {
202
+ [AUTH_MODE_HEADER]: 'session',
203
+ ...(this.usesSession() && { [ACCOUNT_HEADER]: this.sessionAccount() }),
204
+ };
205
+ }
206
+
207
+ private withCurrentAuth(config: AxiosRequestConfig): AxiosRequestConfig {
208
+ delete config.headers['Authorization'];
209
+ delete config.headers[ACCOUNT_HEADER];
210
+ if (this.usesSession()) {
211
+ config.headers[ACCOUNT_HEADER] = this.sessionAccount();
212
+ config.withCredentials = true;
213
+ } else if (this.configuration.accessToken) {
214
+ config.headers['Authorization'] = this.configuration.accessToken;
215
+ }
216
+ return config;
217
+ }
218
+
219
+ // Both keeps the token, so a session call the browser could not complete can be sent again with it.
220
+ private canRetryWithToken(config: AxiosRequestConfig): boolean {
221
+ return !!config.withCredentials && !!config.headers?.[ACCOUNT_HEADER]
222
+ && !!this.tokenData.accessToken && this.tokenData.authMode !== 'session';
223
+ }
224
+
225
+ // A network error can come after the server ran the call, so only a call that is safe to repeat is sent again.
226
+ private canResend(config: AxiosRequestConfig): boolean {
227
+ return RESEND_SAFE_METHODS.indexOf((config.method || 'get').toLowerCase()) !== -1
228
+ || Object.keys(config.headers || {}).some((name) => name.toLowerCase() === 'idempotency-key');
229
+ }
230
+
231
+ private switchToToken() {
232
+ this.useHeaderAuth(this.tokenData.accessToken);
233
+ this.storeTokenData(this.tokenData);
234
+ }
235
+
236
+ private retryWithToken(axios: AxiosInstance, config: AxiosRequestConfig) {
237
+ this.switchToToken();
238
+ config.withCredentials = false;
239
+ return axios(this.withCurrentAuth(config));
240
+ }
241
+
113
242
  selectEnvironment(env: Environment) {
114
243
  this.selectBasePath(env);
115
244
  }
@@ -129,8 +258,8 @@ export class BaseAPI {
129
258
  async authorize(username: string, password: string, targetWorkspace?: string): Promise<void> {
130
259
  const options: AxiosRequestConfig = {
131
260
  method: 'POST',
132
- url: `${this.configuration.basePath}/authservice/v1/login`,
133
- headers: { 'Content-Type': 'application/json' },
261
+ url: `${this.authBasePath()}/authservice/v1/login`,
262
+ headers: { 'Content-Type': 'application/json', ...(this.configuration.cookieAuth && { [AUTH_MODE_HEADER]: 'session' }) },
134
263
  data: {
135
264
  username,
136
265
  password,
@@ -140,13 +269,10 @@ export class BaseAPI {
140
269
 
141
270
  const response = await globalAxios.request<LoginClass>(options);
142
271
 
143
- const { data: { accessToken, permissions } } = response;
144
-
145
272
  this.configuration.username = username;
146
- this.configuration.accessToken = `Bearer ${accessToken}`;
147
273
  this.tokenData.username = username;
148
- this.tokenData.accessToken = accessToken;
149
- this.tokenData.permissions = permissions;
274
+ this.tokenData.permissions = response.data.permissions;
275
+ this.applyTokenReply(response.data);
150
276
 
151
277
  }
152
278
 
@@ -170,9 +296,10 @@ export class BaseAPI {
170
296
 
171
297
  const options: AxiosRequestConfig = {
172
298
  method: 'POST',
173
- url: `${this.configuration.basePath}/authservice/v1/refresh-token`,
299
+ url: `${this.authBasePath()}/authservice/v1/refresh-token`,
174
300
  headers: {
175
301
  'Content-Type': 'application/json',
302
+ ...this.sessionHeaders(),
176
303
  },
177
304
  data: { username: username },
178
305
  withCredentials: true,
@@ -213,20 +340,22 @@ export class BaseAPI {
213
340
  && !originalConfig._retry) {
214
341
  originalConfig._retry = true;
215
342
  try {
216
- const { accessToken: tokenString, permissions } = await this.refreshTokenInternal();
217
- const accessToken = `Bearer ${tokenString}`;
218
- this.tokenData.permissions = permissions;
219
-
220
- delete originalConfig.headers['Authorization']
221
-
222
- originalConfig.headers['Authorization'] = accessToken;
223
-
224
- this.configuration.accessToken = accessToken;
225
- this.tokenData.accessToken = tokenString;
226
-
343
+ const reply = await this.refreshTokenInternal();
344
+ this.tokenData.permissions = reply.permissions;
345
+ this.applyTokenReply(reply);
227
346
  this.storeTokenData(this.tokenData);
228
347
 
229
- return axios(originalConfig);
348
+ if (!this.usesSession()) {
349
+ return axios(this.withCurrentAuth(originalConfig));
350
+ }
351
+
352
+ // Both mode: if the browser does not send the session cookie (another site), use the token.
353
+ return axios(this.withCurrentAuth(originalConfig)).catch((retryError) => {
354
+ if (retryError?.response?.status !== 401 || !this.tokenData.accessToken) {
355
+ return Promise.reject(retryError);
356
+ }
357
+ return this.retryWithToken(axios, originalConfig);
358
+ });
230
359
  } catch (_error) {
231
360
  if (_error.response && _error.response.data) {
232
361
  return Promise.reject(_error.response.data);
@@ -234,26 +363,30 @@ export class BaseAPI {
234
363
  return Promise.reject(_error);
235
364
  }
236
365
  }
366
+ } else if (err.message === NETWORK_ERROR_MESSAGE && this.canRetryWithToken(originalConfig)) {
367
+ // Both mode: the browser blocked the session call or hid its reply; later calls use the token.
368
+ if (!this.canResend(originalConfig)) {
369
+ this.switchToToken();
370
+ return Promise.reject(err);
371
+ }
372
+ return this.retryWithToken(axios, originalConfig);
237
373
  } else if (err.message === NETWORK_ERROR_MESSAGE
238
- && originalConfig.headers.hasOwnProperty('Authorization')
374
+ && (originalConfig.headers.hasOwnProperty('Authorization')
375
+ || (this.configuration.cookieAuth && originalConfig.withCredentials && this.canResend(originalConfig)))
239
376
  && _retry_count < 4
240
377
  ) {
241
378
  _retry_count++;
242
379
  try {
243
- const { accessToken: tokenString, permissions } = await this.refreshTokenInternal();
244
- const accessToken = `Bearer ${tokenString}`;
245
- this.tokenData.permissions = permissions;
380
+ // In session mode an unreadable 401 (no CORS headers on the error reply) also lands here.
381
+ const reply = await this.refreshTokenInternal();
382
+ this.tokenData.permissions = reply.permissions;
246
383
 
247
384
  _retry = true;
248
- originalConfig.headers['Authorization'] = accessToken;
249
-
250
- this.configuration.accessToken = accessToken;
251
- this.tokenData.accessToken = tokenString;
252
-
385
+ this.applyTokenReply(reply);
253
386
  this.storeTokenData(this.tokenData);
254
387
 
255
388
  return axios.request({
256
- ...originalConfig,
389
+ ...this.withCurrentAuth(originalConfig),
257
390
  });
258
391
  } catch (_error) {
259
392
  if (_error.response && _error.response.data) {
package/common.ts CHANGED
@@ -130,9 +130,10 @@ export const toPathString = function (url: URL) {
130
130
  * @export
131
131
  */
132
132
  export const createRequestFunction = function (axiosArgs: RequestArgs, globalAxios: AxiosInstance, BASE_PATH: string, configuration?: Configuration) {
133
- return <T = unknown, R = AxiosResponse<T>>(axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
133
+ // Typed by hand: since axios 1.20, request() returns an internal axios type that SDK declarations cannot name.
134
+ return <T = unknown, R = AxiosResponse<T>>(axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH): Promise<R> => {
134
135
  const axiosRequestArgs = {...axiosArgs.options, url: (configuration?.basePath || basePath) + axiosArgs.url};
135
- return axios.request<T, R>(axiosRequestArgs);
136
+ return axios.request<T, R>(axiosRequestArgs) as Promise<R>;
136
137
  };
137
138
  }
138
139
 
package/configuration.ts CHANGED
@@ -21,6 +21,8 @@ export interface ConfigurationParameters {
21
21
  basePath?: string;
22
22
  baseOptions?: any;
23
23
  formDataCtor?: new () => any;
24
+ cookieAuth?: boolean;
25
+ account?: string;
24
26
  }
25
27
 
26
28
  export class Configuration {
@@ -74,6 +76,22 @@ export class Configuration {
74
76
  */
75
77
  formDataCtor?: new () => any;
76
78
 
79
+ /**
80
+ * Use a server session when authservice makes one; otherwise the token, as without this option.
81
+ * Set `account` too, so each SDK object keeps its own account.
82
+ *
83
+ * @type {boolean}
84
+ * @memberof Configuration
85
+ */
86
+ cookieAuth?: boolean;
87
+ /**
88
+ * The session account (Cognito sub) this app shows, so the SDK never takes another tab's account.
89
+ *
90
+ * @type {string}
91
+ * @memberof Configuration
92
+ */
93
+ account?: string;
94
+
77
95
  constructor(param: ConfigurationParameters = {}) {
78
96
  this.apiKey = param.apiKey;
79
97
  this.username = param.username;
@@ -82,6 +100,8 @@ export class Configuration {
82
100
  this.basePath = param.basePath;
83
101
  this.baseOptions = param.baseOptions;
84
102
  this.formDataCtor = param.formDataCtor;
103
+ this.cookieAuth = param.cookieAuth;
104
+ this.account = param.account;
85
105
  }
86
106
 
87
107
  /**
@@ -66,5 +66,5 @@ export declare class HealthApi extends BaseAPI {
66
66
  * @throws {RequiredError}
67
67
  * @memberof HealthApi
68
68
  */
69
- check(options?: AxiosRequestConfig): Promise<import("axios").AxiosResponse<InlineResponse200, any, {}>>;
69
+ check(options?: AxiosRequestConfig): Promise<import("axios").AxiosResponse<InlineResponse200, any, {}, any>>;
70
70
  }