@emilgroup/account-sdk 1.49.1-beta.12 → 1.49.1-beta.16

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/account-sdk@1.49.1-beta.12 --save
20
+ npm install @emilgroup/account-sdk@1.49.1-beta.16 --save
21
21
  ```
22
22
  or
23
23
  ```
24
- yarn add @emilgroup/account-sdk@1.49.1-beta.12
24
+ yarn add @emilgroup/account-sdk@1.49.1-beta.16
25
25
  ```
26
26
 
27
27
  And then you can import `AccountsApi`.
@@ -67,8 +67,10 @@ export const AccountsApiAxiosParamCreator = function (configuration?: Configurat
67
67
  // http bearer authentication required
68
68
  await setBearerAuthToObject(localVarHeaderParameter, configuration)
69
69
 
70
- if (authorization !== undefined && authorization !== null || baseAccessToken !== undefined && baseAccessToken !== null) {
71
- localVarHeaderParameter['Authorization'] = String(authorization ? authorization : baseAccessToken);
70
+ // Only Authorization falls back to the configured token; an empty value is never sent.
71
+ const authorizationValue = 'Authorization'.toLowerCase() === 'authorization' ? (authorization || baseAccessToken) : authorization;
72
+ if (authorizationValue) {
73
+ localVarHeaderParameter['Authorization'] = String(authorizationValue);
72
74
  }
73
75
 
74
76
 
@@ -115,8 +117,10 @@ export const AccountsApiAxiosParamCreator = function (configuration?: Configurat
115
117
  // http bearer authentication required
116
118
  await setBearerAuthToObject(localVarHeaderParameter, configuration)
117
119
 
118
- if (authorization !== undefined && authorization !== null || baseAccessToken !== undefined && baseAccessToken !== null) {
119
- localVarHeaderParameter['Authorization'] = String(authorization ? authorization : baseAccessToken);
120
+ // Only Authorization falls back to the configured token; an empty value is never sent.
121
+ const authorizationValue = 'Authorization'.toLowerCase() === 'authorization' ? (authorization || baseAccessToken) : authorization;
122
+ if (authorizationValue) {
123
+ localVarHeaderParameter['Authorization'] = String(authorizationValue);
120
124
  }
121
125
 
122
126
 
@@ -165,8 +169,10 @@ export const AccountsApiAxiosParamCreator = function (configuration?: Configurat
165
169
  localVarQueryParameter['expand'] = expand;
166
170
  }
167
171
 
168
- if (authorization !== undefined && authorization !== null || baseAccessToken !== undefined && baseAccessToken !== null) {
169
- localVarHeaderParameter['Authorization'] = String(authorization ? authorization : baseAccessToken);
172
+ // Only Authorization falls back to the configured token; an empty value is never sent.
173
+ const authorizationValue = 'Authorization'.toLowerCase() === 'authorization' ? (authorization || baseAccessToken) : authorization;
174
+ if (authorizationValue) {
175
+ localVarHeaderParameter['Authorization'] = String(authorizationValue);
170
176
  }
171
177
 
172
178
 
@@ -215,8 +221,10 @@ export const AccountsApiAxiosParamCreator = function (configuration?: Configurat
215
221
  localVarQueryParameter['expand'] = expand;
216
222
  }
217
223
 
218
- if (authorization !== undefined && authorization !== null || baseAccessToken !== undefined && baseAccessToken !== null) {
219
- localVarHeaderParameter['Authorization'] = String(authorization ? authorization : baseAccessToken);
224
+ // Only Authorization falls back to the configured token; an empty value is never sent.
225
+ const authorizationValue = 'Authorization'.toLowerCase() === 'authorization' ? (authorization || baseAccessToken) : authorization;
226
+ if (authorizationValue) {
227
+ localVarHeaderParameter['Authorization'] = String(authorizationValue);
220
228
  }
221
229
 
222
230
 
@@ -291,8 +299,10 @@ export const AccountsApiAxiosParamCreator = function (configuration?: Configurat
291
299
  localVarQueryParameter['filters'] = filters;
292
300
  }
293
301
 
294
- if (authorization !== undefined && authorization !== null || baseAccessToken !== undefined && baseAccessToken !== null) {
295
- localVarHeaderParameter['Authorization'] = String(authorization ? authorization : baseAccessToken);
302
+ // Only Authorization falls back to the configured token; an empty value is never sent.
303
+ const authorizationValue = 'Authorization'.toLowerCase() === 'authorization' ? (authorization || baseAccessToken) : authorization;
304
+ if (authorizationValue) {
305
+ localVarHeaderParameter['Authorization'] = String(authorizationValue);
296
306
  }
297
307
 
298
308
 
@@ -339,8 +349,10 @@ export const AccountsApiAxiosParamCreator = function (configuration?: Configurat
339
349
  // http bearer authentication required
340
350
  await setBearerAuthToObject(localVarHeaderParameter, configuration)
341
351
 
342
- if (authorization !== undefined && authorization !== null || baseAccessToken !== undefined && baseAccessToken !== null) {
343
- localVarHeaderParameter['Authorization'] = String(authorization ? authorization : baseAccessToken);
352
+ // Only Authorization falls back to the configured token; an empty value is never sent.
353
+ const authorizationValue = 'Authorization'.toLowerCase() === 'authorization' ? (authorization || baseAccessToken) : authorization;
354
+ if (authorizationValue) {
355
+ localVarHeaderParameter['Authorization'] = String(authorizationValue);
344
356
  }
345
357
 
346
358
 
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
  export interface SwitchWorkspaceRequest {
@@ -45,6 +56,8 @@ export interface SwitchWorkspaceRequest {
45
56
  export interface SwitchWorkspaceResponseClass {
46
57
  accessToken: string;
47
58
  permissions: string;
59
+ authMode?: string;
60
+ session?: SessionClass;
48
61
  }
49
62
 
50
63
  export enum Environment {
@@ -77,10 +90,19 @@ interface TokenData {
77
90
  accessToken?: string;
78
91
  username?: string;
79
92
  permissions?: string;
93
+ // 'both' or 'session' when authservice keeps the tokens in a server session.
94
+ authMode?: string;
95
+ sub?: string;
80
96
  }
81
97
 
82
98
  const NETWORK_ERROR_MESSAGE = "Network Error";
83
99
  const TOKEN_DATA = 'APP_TOKEN';
100
+ // Sent only to authservice, to ask for a server session.
101
+ const AUTH_MODE_HEADER = 'X-Emil-Auth-Mode';
102
+ // Names the account on every call; one browser session can hold several.
103
+ const ACCOUNT_HEADER = 'X-Emil-Account';
104
+ const SESSION_MODES = ['both', 'session'];
105
+ const RESEND_SAFE_METHODS = ['get', 'head', 'options'];
84
106
 
85
107
  /**
86
108
  *
@@ -103,7 +125,11 @@ export class BaseAPI {
103
125
  this.basePath = configuration.basePath || this.basePath;
104
126
 
105
127
  if (configuration.cookieAuth) {
106
- this.useCookieAuth();
128
+ this.configuration.username = this.configuration.username || this.tokenData.username;
129
+ }
130
+
131
+ if (this.usesSession()) {
132
+ this.useSessionAuth();
107
133
  } else {
108
134
  // Use config token if provided, otherwise use tokenData token
109
135
  const configToken = this.configuration.accessToken;
@@ -123,10 +149,105 @@ export class BaseAPI {
123
149
  this.attachInterceptor(axios);
124
150
  }
125
151
 
126
- // Cookie mode: the browser sends the HttpOnly access cookie, so no token is read from storage.
127
- private useCookieAuth() {
128
- this.configuration.username = this.configuration.username || this.tokenData.username;
129
- this.configuration.baseOptions = { ...this.configuration.baseOptions, withCredentials: true };
152
+ // The app's own account wins over APP_TOKEN, which holds whichever tab wrote last.
153
+ private sessionAccount(): string | undefined {
154
+ return this.configuration.account || this.tokenData.sub;
155
+ }
156
+
157
+ // Apps may pass the address as the second argument only; authservice is on the same host.
158
+ private authBasePath(): string {
159
+ return this.configuration.basePath || this.basePath;
160
+ }
161
+
162
+ private usesSession(): boolean {
163
+ return !!this.configuration.cookieAuth && SESSION_MODES.indexOf(this.tokenData.authMode) !== -1 && !!this.sessionAccount();
164
+ }
165
+
166
+ private useSessionAuth() {
167
+ const { headers, ...baseOptions } = this.configuration.baseOptions || {};
168
+ this.configuration.accessToken = '';
169
+ this.configuration.baseOptions = {
170
+ ...baseOptions,
171
+ withCredentials: true,
172
+ headers: { ...headers, [ACCOUNT_HEADER]: this.sessionAccount() },
173
+ };
174
+ }
175
+
176
+ private useHeaderAuth(accessToken: string) {
177
+ this.configuration.accessToken = accessToken ? `Bearer ${accessToken}` : '';
178
+ this.tokenData.accessToken = accessToken;
179
+ delete this.tokenData.authMode;
180
+ delete this.tokenData.sub;
181
+ if (this.configuration.cookieAuth) {
182
+ const { withCredentials, headers, ...baseOptions } = this.configuration.baseOptions || {};
183
+ const { [ACCOUNT_HEADER]: account, ...otherHeaders } = headers || {};
184
+ this.configuration.baseOptions = { ...baseOptions, headers: otherHeaders };
185
+ }
186
+ }
187
+
188
+ private applyTokenReply({ accessToken, authMode, session }: LoginClass) {
189
+ if (this.configuration.cookieAuth && SESSION_MODES.indexOf(authMode) !== -1 && session?.sub) {
190
+ this.tokenData.authMode = authMode;
191
+ this.tokenData.sub = session.sub;
192
+ if (this.configuration.account) {
193
+ // A workspace switch moves the app to another account.
194
+ this.configuration.account = session.sub;
195
+ }
196
+ // Both keeps the token for apps not moved yet; session mode keeps no token at all.
197
+ if (authMode === 'both' && accessToken) {
198
+ this.tokenData.accessToken = accessToken;
199
+ } else {
200
+ delete this.tokenData.accessToken;
201
+ }
202
+ this.useSessionAuth();
203
+ } else {
204
+ this.useHeaderAuth(accessToken);
205
+ }
206
+ }
207
+
208
+ private sessionHeaders(): Record<string, string> {
209
+ if (!this.configuration.cookieAuth) {
210
+ return {};
211
+ }
212
+ return {
213
+ [AUTH_MODE_HEADER]: 'session',
214
+ ...(this.usesSession() && { [ACCOUNT_HEADER]: this.sessionAccount() }),
215
+ };
216
+ }
217
+
218
+ private withCurrentAuth(config: AxiosRequestConfig): AxiosRequestConfig {
219
+ delete config.headers['Authorization'];
220
+ delete config.headers[ACCOUNT_HEADER];
221
+ if (this.usesSession()) {
222
+ config.headers[ACCOUNT_HEADER] = this.sessionAccount();
223
+ config.withCredentials = true;
224
+ } else if (this.configuration.accessToken) {
225
+ config.headers['Authorization'] = this.configuration.accessToken;
226
+ }
227
+ return config;
228
+ }
229
+
230
+ // Both keeps the token, so a session call the browser could not complete can be sent again with it.
231
+ private canRetryWithToken(config: AxiosRequestConfig): boolean {
232
+ return !!config.withCredentials && !!config.headers?.[ACCOUNT_HEADER]
233
+ && !!this.tokenData.accessToken && this.tokenData.authMode !== 'session';
234
+ }
235
+
236
+ // A network error can come after the server ran the call, so only a call that is safe to repeat is sent again.
237
+ private canResend(config: AxiosRequestConfig): boolean {
238
+ return RESEND_SAFE_METHODS.indexOf((config.method || 'get').toLowerCase()) !== -1
239
+ || Object.keys(config.headers || {}).some((name) => name.toLowerCase() === 'idempotency-key');
240
+ }
241
+
242
+ private switchToToken() {
243
+ this.useHeaderAuth(this.tokenData.accessToken);
244
+ this.storeTokenData(this.tokenData);
245
+ }
246
+
247
+ private retryWithToken(axios: AxiosInstance, config: AxiosRequestConfig) {
248
+ this.switchToToken();
249
+ config.withCredentials = false;
250
+ return axios(this.withCurrentAuth(config));
130
251
  }
131
252
 
132
253
  selectEnvironment(env: Environment) {
@@ -148,8 +269,8 @@ export class BaseAPI {
148
269
  async authorize(username: string, password: string, targetWorkspace?: string): Promise<void> {
149
270
  const options: AxiosRequestConfig = {
150
271
  method: 'POST',
151
- url: `${this.configuration.basePath}/authservice/v1/login`,
152
- headers: { 'Content-Type': 'application/json' },
272
+ url: `${this.authBasePath()}/authservice/v1/login`,
273
+ headers: { 'Content-Type': 'application/json', ...(this.configuration.cookieAuth && { [AUTH_MODE_HEADER]: 'session' }) },
153
274
  data: {
154
275
  username,
155
276
  password,
@@ -159,15 +280,10 @@ export class BaseAPI {
159
280
 
160
281
  const response = await globalAxios.request<LoginClass>(options);
161
282
 
162
- const { data: { accessToken, permissions } } = response;
163
-
164
283
  this.configuration.username = username;
165
284
  this.tokenData.username = username;
166
- this.tokenData.permissions = permissions;
167
- if (!this.configuration.cookieAuth) {
168
- this.configuration.accessToken = `Bearer ${accessToken}`;
169
- this.tokenData.accessToken = accessToken;
170
- }
285
+ this.tokenData.permissions = response.data.permissions;
286
+ this.applyTokenReply(response.data);
171
287
 
172
288
  // Switch workspace if provided
173
289
  if (targetWorkspace) {
@@ -187,10 +303,11 @@ export class BaseAPI {
187
303
  async switchWorkspace(targetWorkspace: string): Promise<void> {
188
304
  const options: AxiosRequestConfig = {
189
305
  method: 'POST',
190
- url: `${this.configuration.basePath}/authservice/v1/workspaces/switch`,
306
+ url: `${this.authBasePath()}/authservice/v1/workspaces/switch`,
191
307
  headers: {
192
308
  'Content-Type': 'application/json',
193
- ...(this.configuration.cookieAuth ? {} : { 'Authorization': `Bearer ${this.configuration.accessToken}` }),
309
+ ...this.sessionHeaders(),
310
+ ...(!this.usesSession() && !!this.configuration.accessToken && { 'Authorization': this.configuration.accessToken as string }),
194
311
  },
195
312
  data: {
196
313
  username: this.configuration.username,
@@ -201,13 +318,8 @@ export class BaseAPI {
201
318
 
202
319
  const response = await globalAxios.request<SwitchWorkspaceResponseClass>(options);
203
320
 
204
- const { data: { accessToken, permissions } } = response;
205
-
206
- this.tokenData.permissions = permissions;
207
- if (!this.configuration.cookieAuth) {
208
- this.configuration.accessToken = `Bearer ${accessToken}`;
209
- this.tokenData.accessToken = accessToken;
210
- }
321
+ this.tokenData.permissions = response.data.permissions;
322
+ this.applyTokenReply(response.data);
211
323
 
212
324
  this.storeTokenData({
213
325
  ...this.tokenData
@@ -233,9 +345,10 @@ export class BaseAPI {
233
345
 
234
346
  const options: AxiosRequestConfig = {
235
347
  method: 'POST',
236
- url: `${this.configuration.basePath}/authservice/v1/refresh-token`,
348
+ url: `${this.authBasePath()}/authservice/v1/refresh-token`,
237
349
  headers: {
238
350
  'Content-Type': 'application/json',
351
+ ...this.sessionHeaders(),
239
352
  },
240
353
  data: { username: username },
241
354
  withCredentials: true,
@@ -247,11 +360,6 @@ export class BaseAPI {
247
360
 
248
361
  private storeTokenData(tokenData?: TokenData) {
249
362
  if (typeof window !== 'undefined') {
250
- if (tokenData && this.configuration?.cookieAuth) {
251
- // Cookie mode saves who is logged in, never the token; a token saved by apps not yet migrated stays.
252
- const { username, permissions } = tokenData;
253
- tokenData = { ...(defaultStorage().get<TokenData>(TOKEN_DATA) || {}), username, permissions };
254
- }
255
363
  defaultStorage().set<TokenData>(TOKEN_DATA, tokenData);
256
364
  }
257
365
  }
@@ -281,22 +389,22 @@ export class BaseAPI {
281
389
  && !originalConfig._retry) {
282
390
  originalConfig._retry = true;
283
391
  try {
284
- const { accessToken: tokenString, permissions } = await this.refreshTokenInternal();
285
- this.tokenData.permissions = permissions;
286
-
287
- delete originalConfig.headers['Authorization']
392
+ const reply = await this.refreshTokenInternal();
393
+ this.tokenData.permissions = reply.permissions;
394
+ this.applyTokenReply(reply);
395
+ this.storeTokenData(this.tokenData);
288
396
 
289
- // In cookie mode the refresh reply renewed the access cookie, so retry without a header.
290
- if (!this.configuration.cookieAuth) {
291
- const accessToken = `Bearer ${tokenString}`;
292
- originalConfig.headers['Authorization'] = accessToken;
293
- this.configuration.accessToken = accessToken;
294
- this.tokenData.accessToken = tokenString;
397
+ if (!this.usesSession()) {
398
+ return axios(this.withCurrentAuth(originalConfig));
295
399
  }
296
400
 
297
- this.storeTokenData(this.tokenData);
298
-
299
- return axios(originalConfig);
401
+ // Both mode: if the browser does not send the session cookie (another site), use the token.
402
+ return axios(this.withCurrentAuth(originalConfig)).catch((retryError) => {
403
+ if (retryError?.response?.status !== 401 || !this.tokenData.accessToken) {
404
+ return Promise.reject(retryError);
405
+ }
406
+ return this.retryWithToken(axios, originalConfig);
407
+ });
300
408
  } catch (_error) {
301
409
  if (_error.response && _error.response.data) {
302
410
  return Promise.reject(_error.response.data);
@@ -304,26 +412,30 @@ export class BaseAPI {
304
412
  return Promise.reject(_error);
305
413
  }
306
414
  }
415
+ } else if (err.message === NETWORK_ERROR_MESSAGE && this.canRetryWithToken(originalConfig)) {
416
+ // Both mode: the browser blocked the session call or hid its reply; later calls use the token.
417
+ if (!this.canResend(originalConfig)) {
418
+ this.switchToToken();
419
+ return Promise.reject(err);
420
+ }
421
+ return this.retryWithToken(axios, originalConfig);
307
422
  } else if (err.message === NETWORK_ERROR_MESSAGE
308
- && originalConfig.headers.hasOwnProperty('Authorization')
423
+ && (originalConfig.headers.hasOwnProperty('Authorization')
424
+ || (this.configuration.cookieAuth && originalConfig.withCredentials && this.canResend(originalConfig)))
309
425
  && _retry_count < 4
310
426
  ) {
311
427
  _retry_count++;
312
428
  try {
313
- const { accessToken: tokenString, permissions } = await this.refreshTokenInternal();
314
- const accessToken = `Bearer ${tokenString}`;
315
- this.tokenData.permissions = permissions;
429
+ // In session mode an unreadable 401 (no CORS headers on the error reply) also lands here.
430
+ const reply = await this.refreshTokenInternal();
431
+ this.tokenData.permissions = reply.permissions;
316
432
 
317
433
  _retry = true;
318
- originalConfig.headers['Authorization'] = accessToken;
319
-
320
- this.configuration.accessToken = accessToken;
321
- this.tokenData.accessToken = tokenString;
322
-
434
+ this.applyTokenReply(reply);
323
435
  this.storeTokenData(this.tokenData);
324
436
 
325
437
  return axios.request({
326
- ...originalConfig,
438
+ ...this.withCurrentAuth(originalConfig),
327
439
  });
328
440
  } catch (_error) {
329
441
  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
@@ -22,6 +22,7 @@ export interface ConfigurationParameters {
22
22
  baseOptions?: any;
23
23
  formDataCtor?: new () => any;
24
24
  cookieAuth?: boolean;
25
+ account?: string;
25
26
  }
26
27
 
27
28
  export class Configuration {
@@ -76,13 +77,20 @@ export class Configuration {
76
77
  formDataCtor?: new () => any;
77
78
 
78
79
  /**
79
- * Authenticate with the HttpOnly access cookie instead of a stored token: every call sends credentials.
80
- * The browser holds one cookie, so use it with one logged-in account; log out through the auth service.
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.
81
82
  *
82
83
  * @type {boolean}
83
84
  * @memberof Configuration
84
85
  */
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;
86
94
 
87
95
  constructor(param: ConfigurationParameters = {}) {
88
96
  this.apiKey = param.apiKey;
@@ -93,6 +101,7 @@ export class Configuration {
93
101
  this.baseOptions = param.baseOptions;
94
102
  this.formDataCtor = param.formDataCtor;
95
103
  this.cookieAuth = param.cookieAuth;
104
+ this.account = param.account;
96
105
  }
97
106
 
98
107
  /**
@@ -408,7 +408,7 @@ export declare class AccountsApi extends BaseAPI {
408
408
  * @throws {RequiredError}
409
409
  * @memberof AccountsApi
410
410
  */
411
- createAccount(requestParameters: AccountsApiCreateAccountRequest, options?: AxiosRequestConfig): Promise<import("axios").AxiosResponse<CreateAccountResponseClass, any, {}>>;
411
+ createAccount(requestParameters: AccountsApiCreateAccountRequest, options?: AxiosRequestConfig): Promise<import("axios").AxiosResponse<CreateAccountResponseClass, any, {}, any>>;
412
412
  /**
413
413
  * Permanently deletes the account. Supply the unique code that was returned when you created the account and this will delete it. **Required Permissions** \"partner-management.partners.delete\"
414
414
  * @summary Delete the account
@@ -417,7 +417,7 @@ export declare class AccountsApi extends BaseAPI {
417
417
  * @throws {RequiredError}
418
418
  * @memberof AccountsApi
419
419
  */
420
- deleteAccount(requestParameters: AccountsApiDeleteAccountRequest, options?: AxiosRequestConfig): Promise<import("axios").AxiosResponse<void, any, {}>>;
420
+ deleteAccount(requestParameters: AccountsApiDeleteAccountRequest, options?: AxiosRequestConfig): Promise<import("axios").AxiosResponse<void, any, {}, any>>;
421
421
  /**
422
422
  * Retrieves the details of the account that was previously created. Supply the unique account code that was returned when you created it and Emil Api will return the corresponding account information. **Required Permissions** \"partner-management.partners.view\"
423
423
  * @summary Retrieve the account
@@ -426,7 +426,7 @@ export declare class AccountsApi extends BaseAPI {
426
426
  * @throws {RequiredError}
427
427
  * @memberof AccountsApi
428
428
  */
429
- getAccount(requestParameters: AccountsApiGetAccountRequest, options?: AxiosRequestConfig): Promise<import("axios").AxiosResponse<GetAccountResponseClass, any, {}>>;
429
+ getAccount(requestParameters: AccountsApiGetAccountRequest, options?: AxiosRequestConfig): Promise<import("axios").AxiosResponse<GetAccountResponseClass, any, {}, any>>;
430
430
  /**
431
431
  * Retrieves the details of the account that was previously created. Supply the email that was returned when you created it and Emil Api will return the corresponding account information. Email matching is case-insensitive. **Required Permissions** \"partner-management.partners.view\"
432
432
  * @summary Retrieve the account
@@ -435,7 +435,7 @@ export declare class AccountsApi extends BaseAPI {
435
435
  * @throws {RequiredError}
436
436
  * @memberof AccountsApi
437
437
  */
438
- getAccountByEmail(requestParameters: AccountsApiGetAccountByEmailRequest, options?: AxiosRequestConfig): Promise<import("axios").AxiosResponse<GetAccountResponseClass, any, {}>>;
438
+ getAccountByEmail(requestParameters: AccountsApiGetAccountByEmailRequest, options?: AxiosRequestConfig): Promise<import("axios").AxiosResponse<GetAccountResponseClass, any, {}, any>>;
439
439
  /**
440
440
  * Returns a list of accounts you have previously created. The accounts are returned in sorted order, with the oldest one appearing first. For more information about pagination, read the Pagination documentation. **Required Permissions** \"partner-management.partners.view\"
441
441
  * @summary List accounts
@@ -444,7 +444,7 @@ export declare class AccountsApi extends BaseAPI {
444
444
  * @throws {RequiredError}
445
445
  * @memberof AccountsApi
446
446
  */
447
- listAccounts(requestParameters?: AccountsApiListAccountsRequest, options?: AxiosRequestConfig): Promise<import("axios").AxiosResponse<ListAccountsResponseClass, any, {}>>;
447
+ listAccounts(requestParameters?: AccountsApiListAccountsRequest, options?: AxiosRequestConfig): Promise<import("axios").AxiosResponse<ListAccountsResponseClass, any, {}, any>>;
448
448
  /**
449
449
  * Updates the specified account by setting the values of the parameters passed. Any parameters not provided will be left unchanged. **Required Permissions** \"partner-management.partners.update\"
450
450
  * @summary Update the account
@@ -453,5 +453,5 @@ export declare class AccountsApi extends BaseAPI {
453
453
  * @throws {RequiredError}
454
454
  * @memberof AccountsApi
455
455
  */
456
- updateAccount(requestParameters: AccountsApiUpdateAccountRequest, options?: AxiosRequestConfig): Promise<import("axios").AxiosResponse<UpdateAccountResponseClass, any, {}>>;
456
+ updateAccount(requestParameters: AccountsApiUpdateAccountRequest, options?: AxiosRequestConfig): Promise<import("axios").AxiosResponse<UpdateAccountResponseClass, any, {}, any>>;
457
457
  }
@@ -103,7 +103,7 @@ var AccountsApiAxiosParamCreator = function (configuration) {
103
103
  createAccount: function (createAccountRequestDto, authorization, options) {
104
104
  if (options === void 0) { options = {}; }
105
105
  return __awaiter(_this, void 0, void 0, function () {
106
- var localVarPath, localVarUrlObj, baseOptions, baseAccessToken, localVarRequestOptions, localVarHeaderParameter, localVarQueryParameter, headersFromBaseOptions;
106
+ var localVarPath, localVarUrlObj, baseOptions, baseAccessToken, localVarRequestOptions, localVarHeaderParameter, localVarQueryParameter, authorizationValue, headersFromBaseOptions;
107
107
  return __generator(this, function (_a) {
108
108
  switch (_a.label) {
109
109
  case 0:
@@ -120,13 +120,16 @@ var AccountsApiAxiosParamCreator = function (configuration) {
120
120
  localVarQueryParameter = {};
121
121
  // authentication bearer required
122
122
  // http bearer authentication required
123
- return [4 /*yield*/, (0, common_1.setBearerAuthToObject)(localVarHeaderParameter, configuration)];
123
+ return [4 /*yield*/, (0, common_1.setBearerAuthToObject)(localVarHeaderParameter, configuration)
124
+ // Only Authorization falls back to the configured token; an empty value is never sent.
125
+ ];
124
126
  case 1:
125
127
  // authentication bearer required
126
128
  // http bearer authentication required
127
129
  _a.sent();
128
- if (authorization !== undefined && authorization !== null || baseAccessToken !== undefined && baseAccessToken !== null) {
129
- localVarHeaderParameter['Authorization'] = String(authorization ? authorization : baseAccessToken);
130
+ authorizationValue = 'Authorization'.toLowerCase() === 'authorization' ? (authorization || baseAccessToken) : authorization;
131
+ if (authorizationValue) {
132
+ localVarHeaderParameter['Authorization'] = String(authorizationValue);
130
133
  }
131
134
  localVarHeaderParameter['Content-Type'] = 'application/json';
132
135
  (0, common_1.setSearchParams)(localVarUrlObj, localVarQueryParameter);
@@ -152,7 +155,7 @@ var AccountsApiAxiosParamCreator = function (configuration) {
152
155
  deleteAccount: function (code, authorization, options) {
153
156
  if (options === void 0) { options = {}; }
154
157
  return __awaiter(_this, void 0, void 0, function () {
155
- var localVarPath, localVarUrlObj, baseOptions, baseAccessToken, localVarRequestOptions, localVarHeaderParameter, localVarQueryParameter, headersFromBaseOptions;
158
+ var localVarPath, localVarUrlObj, baseOptions, baseAccessToken, localVarRequestOptions, localVarHeaderParameter, localVarQueryParameter, authorizationValue, headersFromBaseOptions;
156
159
  return __generator(this, function (_a) {
157
160
  switch (_a.label) {
158
161
  case 0:
@@ -170,13 +173,16 @@ var AccountsApiAxiosParamCreator = function (configuration) {
170
173
  localVarQueryParameter = {};
171
174
  // authentication bearer required
172
175
  // http bearer authentication required
173
- return [4 /*yield*/, (0, common_1.setBearerAuthToObject)(localVarHeaderParameter, configuration)];
176
+ return [4 /*yield*/, (0, common_1.setBearerAuthToObject)(localVarHeaderParameter, configuration)
177
+ // Only Authorization falls back to the configured token; an empty value is never sent.
178
+ ];
174
179
  case 1:
175
180
  // authentication bearer required
176
181
  // http bearer authentication required
177
182
  _a.sent();
178
- if (authorization !== undefined && authorization !== null || baseAccessToken !== undefined && baseAccessToken !== null) {
179
- localVarHeaderParameter['Authorization'] = String(authorization ? authorization : baseAccessToken);
183
+ authorizationValue = 'Authorization'.toLowerCase() === 'authorization' ? (authorization || baseAccessToken) : authorization;
184
+ if (authorizationValue) {
185
+ localVarHeaderParameter['Authorization'] = String(authorizationValue);
180
186
  }
181
187
  (0, common_1.setSearchParams)(localVarUrlObj, localVarQueryParameter);
182
188
  headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
@@ -201,7 +207,7 @@ var AccountsApiAxiosParamCreator = function (configuration) {
201
207
  getAccount: function (code, authorization, expand, options) {
202
208
  if (options === void 0) { options = {}; }
203
209
  return __awaiter(_this, void 0, void 0, function () {
204
- var localVarPath, localVarUrlObj, baseOptions, baseAccessToken, localVarRequestOptions, localVarHeaderParameter, localVarQueryParameter, headersFromBaseOptions;
210
+ var localVarPath, localVarUrlObj, baseOptions, baseAccessToken, localVarRequestOptions, localVarHeaderParameter, localVarQueryParameter, authorizationValue, headersFromBaseOptions;
205
211
  return __generator(this, function (_a) {
206
212
  switch (_a.label) {
207
213
  case 0:
@@ -227,8 +233,9 @@ var AccountsApiAxiosParamCreator = function (configuration) {
227
233
  if (expand !== undefined) {
228
234
  localVarQueryParameter['expand'] = expand;
229
235
  }
230
- if (authorization !== undefined && authorization !== null || baseAccessToken !== undefined && baseAccessToken !== null) {
231
- localVarHeaderParameter['Authorization'] = String(authorization ? authorization : baseAccessToken);
236
+ authorizationValue = 'Authorization'.toLowerCase() === 'authorization' ? (authorization || baseAccessToken) : authorization;
237
+ if (authorizationValue) {
238
+ localVarHeaderParameter['Authorization'] = String(authorizationValue);
232
239
  }
233
240
  (0, common_1.setSearchParams)(localVarUrlObj, localVarQueryParameter);
234
241
  headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
@@ -253,7 +260,7 @@ var AccountsApiAxiosParamCreator = function (configuration) {
253
260
  getAccountByEmail: function (email, authorization, expand, options) {
254
261
  if (options === void 0) { options = {}; }
255
262
  return __awaiter(_this, void 0, void 0, function () {
256
- var localVarPath, localVarUrlObj, baseOptions, baseAccessToken, localVarRequestOptions, localVarHeaderParameter, localVarQueryParameter, headersFromBaseOptions;
263
+ var localVarPath, localVarUrlObj, baseOptions, baseAccessToken, localVarRequestOptions, localVarHeaderParameter, localVarQueryParameter, authorizationValue, headersFromBaseOptions;
257
264
  return __generator(this, function (_a) {
258
265
  switch (_a.label) {
259
266
  case 0:
@@ -279,8 +286,9 @@ var AccountsApiAxiosParamCreator = function (configuration) {
279
286
  if (expand !== undefined) {
280
287
  localVarQueryParameter['expand'] = expand;
281
288
  }
282
- if (authorization !== undefined && authorization !== null || baseAccessToken !== undefined && baseAccessToken !== null) {
283
- localVarHeaderParameter['Authorization'] = String(authorization ? authorization : baseAccessToken);
289
+ authorizationValue = 'Authorization'.toLowerCase() === 'authorization' ? (authorization || baseAccessToken) : authorization;
290
+ if (authorizationValue) {
291
+ localVarHeaderParameter['Authorization'] = String(authorizationValue);
284
292
  }
285
293
  (0, common_1.setSearchParams)(localVarUrlObj, localVarQueryParameter);
286
294
  headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
@@ -310,7 +318,7 @@ var AccountsApiAxiosParamCreator = function (configuration) {
310
318
  listAccounts: function (authorization, pageSize, pageToken, filter, search, order, expand, filters, options) {
311
319
  if (options === void 0) { options = {}; }
312
320
  return __awaiter(_this, void 0, void 0, function () {
313
- var localVarPath, localVarUrlObj, baseOptions, baseAccessToken, localVarRequestOptions, localVarHeaderParameter, localVarQueryParameter, headersFromBaseOptions;
321
+ var localVarPath, localVarUrlObj, baseOptions, baseAccessToken, localVarRequestOptions, localVarHeaderParameter, localVarQueryParameter, authorizationValue, headersFromBaseOptions;
314
322
  return __generator(this, function (_a) {
315
323
  switch (_a.label) {
316
324
  case 0:
@@ -351,8 +359,9 @@ var AccountsApiAxiosParamCreator = function (configuration) {
351
359
  if (filters !== undefined) {
352
360
  localVarQueryParameter['filters'] = filters;
353
361
  }
354
- if (authorization !== undefined && authorization !== null || baseAccessToken !== undefined && baseAccessToken !== null) {
355
- localVarHeaderParameter['Authorization'] = String(authorization ? authorization : baseAccessToken);
362
+ authorizationValue = 'Authorization'.toLowerCase() === 'authorization' ? (authorization || baseAccessToken) : authorization;
363
+ if (authorizationValue) {
364
+ localVarHeaderParameter['Authorization'] = String(authorizationValue);
356
365
  }
357
366
  (0, common_1.setSearchParams)(localVarUrlObj, localVarQueryParameter);
358
367
  headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
@@ -377,7 +386,7 @@ var AccountsApiAxiosParamCreator = function (configuration) {
377
386
  updateAccount: function (code, updateAccountRequestDto, authorization, options) {
378
387
  if (options === void 0) { options = {}; }
379
388
  return __awaiter(_this, void 0, void 0, function () {
380
- var localVarPath, localVarUrlObj, baseOptions, baseAccessToken, localVarRequestOptions, localVarHeaderParameter, localVarQueryParameter, headersFromBaseOptions;
389
+ var localVarPath, localVarUrlObj, baseOptions, baseAccessToken, localVarRequestOptions, localVarHeaderParameter, localVarQueryParameter, authorizationValue, headersFromBaseOptions;
381
390
  return __generator(this, function (_a) {
382
391
  switch (_a.label) {
383
392
  case 0:
@@ -397,13 +406,16 @@ var AccountsApiAxiosParamCreator = function (configuration) {
397
406
  localVarQueryParameter = {};
398
407
  // authentication bearer required
399
408
  // http bearer authentication required
400
- return [4 /*yield*/, (0, common_1.setBearerAuthToObject)(localVarHeaderParameter, configuration)];
409
+ return [4 /*yield*/, (0, common_1.setBearerAuthToObject)(localVarHeaderParameter, configuration)
410
+ // Only Authorization falls back to the configured token; an empty value is never sent.
411
+ ];
401
412
  case 1:
402
413
  // authentication bearer required
403
414
  // http bearer authentication required
404
415
  _a.sent();
405
- if (authorization !== undefined && authorization !== null || baseAccessToken !== undefined && baseAccessToken !== null) {
406
- localVarHeaderParameter['Authorization'] = String(authorization ? authorization : baseAccessToken);
416
+ authorizationValue = 'Authorization'.toLowerCase() === 'authorization' ? (authorization || baseAccessToken) : authorization;
417
+ if (authorizationValue) {
418
+ localVarHeaderParameter['Authorization'] = String(authorizationValue);
407
419
  }
408
420
  localVarHeaderParameter['Content-Type'] = 'application/json';
409
421
  (0, common_1.setSearchParams)(localVarUrlObj, localVarQueryParameter);
@@ -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
  }
package/dist/base.d.ts CHANGED
@@ -22,9 +22,19 @@ export declare const COLLECTION_FORMATS: {
22
22
  tsv: string;
23
23
  pipes: string;
24
24
  };
25
+ export interface SessionClass {
26
+ sub: string;
27
+ username?: string;
28
+ email?: string;
29
+ tenantSlug?: string;
30
+ permissions?: string;
31
+ expiresAt?: number;
32
+ }
25
33
  export interface LoginClass {
26
34
  accessToken: string;
27
35
  permissions: string;
36
+ authMode?: string;
37
+ session?: SessionClass;
28
38
  }
29
39
  export interface SwitchWorkspaceRequest {
30
40
  username: string;
@@ -33,6 +43,8 @@ export interface SwitchWorkspaceRequest {
33
43
  export interface SwitchWorkspaceResponseClass {
34
44
  accessToken: string;
35
45
  permissions: string;
46
+ authMode?: string;
47
+ session?: SessionClass;
36
48
  }
37
49
  export declare enum Environment {
38
50
  Production = "https://apiv2.emil.de",
@@ -63,7 +75,18 @@ export declare class BaseAPI {
63
75
  protected configuration: Configuration | undefined;
64
76
  private tokenData?;
65
77
  constructor(configuration?: Configuration, basePath?: string, axios?: AxiosInstance);
66
- private useCookieAuth;
78
+ private sessionAccount;
79
+ private authBasePath;
80
+ private usesSession;
81
+ private useSessionAuth;
82
+ private useHeaderAuth;
83
+ private applyTokenReply;
84
+ private sessionHeaders;
85
+ private withCurrentAuth;
86
+ private canRetryWithToken;
87
+ private canResend;
88
+ private switchToToken;
89
+ private retryWithToken;
67
90
  selectEnvironment(env: Environment): void;
68
91
  selectBasePath(path: string): void;
69
92
  getPermissions(): Array<string>;
package/dist/base.js CHANGED
@@ -74,6 +74,17 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
74
74
  if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
75
75
  }
76
76
  };
77
+ var __rest = (this && this.__rest) || function (s, e) {
78
+ var t = {};
79
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
80
+ t[p] = s[p];
81
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
82
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
83
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
84
+ t[p[i]] = s[p[i]];
85
+ }
86
+ return t;
87
+ };
77
88
  var __importDefault = (this && this.__importDefault) || function (mod) {
78
89
  return (mod && mod.__esModule) ? mod : { "default": mod };
79
90
  };
@@ -112,6 +123,12 @@ function resetRetry() {
112
123
  exports.resetRetry = resetRetry;
113
124
  var NETWORK_ERROR_MESSAGE = "Network Error";
114
125
  var TOKEN_DATA = 'APP_TOKEN';
126
+ // Sent only to authservice, to ask for a server session.
127
+ var AUTH_MODE_HEADER = 'X-Emil-Auth-Mode';
128
+ // Names the account on every call; one browser session can hold several.
129
+ var ACCOUNT_HEADER = 'X-Emil-Account';
130
+ var SESSION_MODES = ['both', 'session'];
131
+ var RESEND_SAFE_METHODS = ['get', 'head', 'options'];
115
132
  /**
116
133
  *
117
134
  * @export
@@ -129,7 +146,10 @@ var BaseAPI = /** @class */ (function () {
129
146
  this.configuration = configuration;
130
147
  this.basePath = configuration.basePath || this.basePath;
131
148
  if (configuration.cookieAuth) {
132
- this.useCookieAuth();
149
+ this.configuration.username = this.configuration.username || this.tokenData.username;
150
+ }
151
+ if (this.usesSession()) {
152
+ this.useSessionAuth();
133
153
  }
134
154
  else {
135
155
  // Use config token if provided, otherwise use tokenData token
@@ -148,10 +168,94 @@ var BaseAPI = /** @class */ (function () {
148
168
  }
149
169
  this.attachInterceptor(axios);
150
170
  }
151
- // Cookie mode: the browser sends the HttpOnly access cookie, so no token is read from storage.
152
- BaseAPI.prototype.useCookieAuth = function () {
153
- this.configuration.username = this.configuration.username || this.tokenData.username;
154
- this.configuration.baseOptions = __assign(__assign({}, this.configuration.baseOptions), { withCredentials: true });
171
+ // The app's own account wins over APP_TOKEN, which holds whichever tab wrote last.
172
+ BaseAPI.prototype.sessionAccount = function () {
173
+ return this.configuration.account || this.tokenData.sub;
174
+ };
175
+ // Apps may pass the address as the second argument only; authservice is on the same host.
176
+ BaseAPI.prototype.authBasePath = function () {
177
+ return this.configuration.basePath || this.basePath;
178
+ };
179
+ BaseAPI.prototype.usesSession = function () {
180
+ return !!this.configuration.cookieAuth && SESSION_MODES.indexOf(this.tokenData.authMode) !== -1 && !!this.sessionAccount();
181
+ };
182
+ BaseAPI.prototype.useSessionAuth = function () {
183
+ var _a;
184
+ var _b = this.configuration.baseOptions || {}, headers = _b.headers, baseOptions = __rest(_b, ["headers"]);
185
+ this.configuration.accessToken = '';
186
+ this.configuration.baseOptions = __assign(__assign({}, baseOptions), { withCredentials: true, headers: __assign(__assign({}, headers), (_a = {}, _a[ACCOUNT_HEADER] = this.sessionAccount(), _a)) });
187
+ };
188
+ BaseAPI.prototype.useHeaderAuth = function (accessToken) {
189
+ this.configuration.accessToken = accessToken ? "Bearer ".concat(accessToken) : '';
190
+ this.tokenData.accessToken = accessToken;
191
+ delete this.tokenData.authMode;
192
+ delete this.tokenData.sub;
193
+ if (this.configuration.cookieAuth) {
194
+ var _a = this.configuration.baseOptions || {}, withCredentials = _a.withCredentials, headers = _a.headers, baseOptions = __rest(_a, ["withCredentials", "headers"]);
195
+ var _b = headers || {}, _c = ACCOUNT_HEADER, account = _b[_c], otherHeaders = __rest(_b, [typeof _c === "symbol" ? _c : _c + ""]);
196
+ this.configuration.baseOptions = __assign(__assign({}, baseOptions), { headers: otherHeaders });
197
+ }
198
+ };
199
+ BaseAPI.prototype.applyTokenReply = function (_a) {
200
+ var accessToken = _a.accessToken, authMode = _a.authMode, session = _a.session;
201
+ if (this.configuration.cookieAuth && SESSION_MODES.indexOf(authMode) !== -1 && (session === null || session === void 0 ? void 0 : session.sub)) {
202
+ this.tokenData.authMode = authMode;
203
+ this.tokenData.sub = session.sub;
204
+ if (this.configuration.account) {
205
+ // A workspace switch moves the app to another account.
206
+ this.configuration.account = session.sub;
207
+ }
208
+ // Both keeps the token for apps not moved yet; session mode keeps no token at all.
209
+ if (authMode === 'both' && accessToken) {
210
+ this.tokenData.accessToken = accessToken;
211
+ }
212
+ else {
213
+ delete this.tokenData.accessToken;
214
+ }
215
+ this.useSessionAuth();
216
+ }
217
+ else {
218
+ this.useHeaderAuth(accessToken);
219
+ }
220
+ };
221
+ BaseAPI.prototype.sessionHeaders = function () {
222
+ var _a, _b;
223
+ if (!this.configuration.cookieAuth) {
224
+ return {};
225
+ }
226
+ return __assign((_a = {}, _a[AUTH_MODE_HEADER] = 'session', _a), (this.usesSession() && (_b = {}, _b[ACCOUNT_HEADER] = this.sessionAccount(), _b)));
227
+ };
228
+ BaseAPI.prototype.withCurrentAuth = function (config) {
229
+ delete config.headers['Authorization'];
230
+ delete config.headers[ACCOUNT_HEADER];
231
+ if (this.usesSession()) {
232
+ config.headers[ACCOUNT_HEADER] = this.sessionAccount();
233
+ config.withCredentials = true;
234
+ }
235
+ else if (this.configuration.accessToken) {
236
+ config.headers['Authorization'] = this.configuration.accessToken;
237
+ }
238
+ return config;
239
+ };
240
+ // Both keeps the token, so a session call the browser could not complete can be sent again with it.
241
+ BaseAPI.prototype.canRetryWithToken = function (config) {
242
+ var _a;
243
+ return !!config.withCredentials && !!((_a = config.headers) === null || _a === void 0 ? void 0 : _a[ACCOUNT_HEADER])
244
+ && !!this.tokenData.accessToken && this.tokenData.authMode !== 'session';
245
+ };
246
+ // A network error can come after the server ran the call, so only a call that is safe to repeat is sent again.
247
+ BaseAPI.prototype.canResend = function (config) {
248
+ return RESEND_SAFE_METHODS.indexOf((config.method || 'get').toLowerCase()) !== -1
249
+ || Object.keys(config.headers || {}).some(function (name) { return name.toLowerCase() === 'idempotency-key'; });
250
+ };
251
+ BaseAPI.prototype.switchToToken = function () {
252
+ this.useHeaderAuth(this.tokenData.accessToken);
253
+ this.storeTokenData(this.tokenData);
254
+ };
255
+ BaseAPI.prototype.retryWithToken = function (axios, config) {
256
+ this.switchToToken();
257
+ config.withCredentials = false;
258
+ return axios(this.withCurrentAuth(config));
155
259
  };
156
260
  BaseAPI.prototype.selectEnvironment = function (env) {
157
261
  this.selectBasePath(env);
@@ -168,14 +272,15 @@ var BaseAPI = /** @class */ (function () {
168
272
  };
169
273
  BaseAPI.prototype.authorize = function (username, password, targetWorkspace) {
170
274
  return __awaiter(this, void 0, void 0, function () {
171
- var options, response, _a, accessToken, permissions;
275
+ var options, response;
276
+ var _a;
172
277
  return __generator(this, function (_b) {
173
278
  switch (_b.label) {
174
279
  case 0:
175
280
  options = {
176
281
  method: 'POST',
177
- url: "".concat(this.configuration.basePath, "/authservice/v1/login"),
178
- headers: { 'Content-Type': 'application/json' },
282
+ url: "".concat(this.authBasePath(), "/authservice/v1/login"),
283
+ headers: __assign({ 'Content-Type': 'application/json' }, (this.configuration.cookieAuth && (_a = {}, _a[AUTH_MODE_HEADER] = 'session', _a))),
179
284
  data: {
180
285
  username: username,
181
286
  password: password,
@@ -185,14 +290,10 @@ var BaseAPI = /** @class */ (function () {
185
290
  return [4 /*yield*/, axios_1.default.request(options)];
186
291
  case 1:
187
292
  response = _b.sent();
188
- _a = response.data, accessToken = _a.accessToken, permissions = _a.permissions;
189
293
  this.configuration.username = username;
190
294
  this.tokenData.username = username;
191
- this.tokenData.permissions = permissions;
192
- if (!this.configuration.cookieAuth) {
193
- this.configuration.accessToken = "Bearer ".concat(accessToken);
194
- this.tokenData.accessToken = accessToken;
195
- }
295
+ this.tokenData.permissions = response.data.permissions;
296
+ this.applyTokenReply(response.data);
196
297
  if (!targetWorkspace) return [3 /*break*/, 3];
197
298
  return [4 /*yield*/, this.switchWorkspace(targetWorkspace)];
198
299
  case 2:
@@ -211,14 +312,14 @@ var BaseAPI = /** @class */ (function () {
211
312
  };
212
313
  BaseAPI.prototype.switchWorkspace = function (targetWorkspace) {
213
314
  return __awaiter(this, void 0, void 0, function () {
214
- var options, response, _a, accessToken, permissions;
215
- return __generator(this, function (_b) {
216
- switch (_b.label) {
315
+ var options, response;
316
+ return __generator(this, function (_a) {
317
+ switch (_a.label) {
217
318
  case 0:
218
319
  options = {
219
320
  method: 'POST',
220
- url: "".concat(this.configuration.basePath, "/authservice/v1/workspaces/switch"),
221
- headers: __assign({ 'Content-Type': 'application/json' }, (this.configuration.cookieAuth ? {} : { 'Authorization': "Bearer ".concat(this.configuration.accessToken) })),
321
+ url: "".concat(this.authBasePath(), "/authservice/v1/workspaces/switch"),
322
+ headers: __assign(__assign({ 'Content-Type': 'application/json' }, this.sessionHeaders()), (!this.usesSession() && !!this.configuration.accessToken && { 'Authorization': this.configuration.accessToken })),
222
323
  data: {
223
324
  username: this.configuration.username,
224
325
  targetWorkspace: targetWorkspace,
@@ -227,13 +328,9 @@ var BaseAPI = /** @class */ (function () {
227
328
  };
228
329
  return [4 /*yield*/, axios_1.default.request(options)];
229
330
  case 1:
230
- response = _b.sent();
231
- _a = response.data, accessToken = _a.accessToken, permissions = _a.permissions;
232
- this.tokenData.permissions = permissions;
233
- if (!this.configuration.cookieAuth) {
234
- this.configuration.accessToken = "Bearer ".concat(accessToken);
235
- this.tokenData.accessToken = accessToken;
236
- }
331
+ response = _a.sent();
332
+ this.tokenData.permissions = response.data.permissions;
333
+ this.applyTokenReply(response.data);
237
334
  this.storeTokenData(__assign({}, this.tokenData));
238
335
  return [2 /*return*/];
239
336
  }
@@ -260,10 +357,8 @@ var BaseAPI = /** @class */ (function () {
260
357
  });
261
358
  options = {
262
359
  method: 'POST',
263
- url: "".concat(this.configuration.basePath, "/authservice/v1/refresh-token"),
264
- headers: {
265
- 'Content-Type': 'application/json',
266
- },
360
+ url: "".concat(this.authBasePath(), "/authservice/v1/refresh-token"),
361
+ headers: __assign({ 'Content-Type': 'application/json' }, this.sessionHeaders()),
267
362
  data: { username: username },
268
363
  withCredentials: true,
269
364
  };
@@ -276,13 +371,7 @@ var BaseAPI = /** @class */ (function () {
276
371
  });
277
372
  };
278
373
  BaseAPI.prototype.storeTokenData = function (tokenData) {
279
- var _a;
280
374
  if (typeof window !== 'undefined') {
281
- if (tokenData && ((_a = this.configuration) === null || _a === void 0 ? void 0 : _a.cookieAuth)) {
282
- // Cookie mode saves who is logged in, never the token; a token saved by apps not yet migrated stays.
283
- var username = tokenData.username, permissions = tokenData.permissions;
284
- tokenData = __assign(__assign({}, ((0, common_1.defaultStorage)().get(TOKEN_DATA) || {})), { username: username, permissions: permissions });
285
- }
286
375
  (0, common_1.defaultStorage)().set(TOKEN_DATA, tokenData);
287
376
  }
288
377
  };
@@ -302,65 +391,75 @@ var BaseAPI = /** @class */ (function () {
302
391
  axios.interceptors.response.use(function (res) {
303
392
  return res;
304
393
  }, function (err) { return __awaiter(_this, void 0, void 0, function () {
305
- var originalConfig, _a, tokenString, permissions, accessToken, _error_1, _b, tokenString, permissions, accessToken, _error_2;
306
- return __generator(this, function (_c) {
307
- switch (_c.label) {
394
+ var originalConfig, reply, _error_1, reply, _error_2;
395
+ var _this = this;
396
+ return __generator(this, function (_a) {
397
+ switch (_a.label) {
308
398
  case 0:
309
399
  originalConfig = err.config;
310
400
  if (!(err.response && !(err.response instanceof XMLHttpRequest))) return [3 /*break*/, 5];
311
401
  if (!((err.response.status === 401 || err.response.status === 403)
312
402
  && !originalConfig._retry)) return [3 /*break*/, 4];
313
403
  originalConfig._retry = true;
314
- _c.label = 1;
404
+ _a.label = 1;
315
405
  case 1:
316
- _c.trys.push([1, 3, , 4]);
406
+ _a.trys.push([1, 3, , 4]);
317
407
  return [4 /*yield*/, this.refreshTokenInternal()];
318
408
  case 2:
319
- _a = _c.sent(), tokenString = _a.accessToken, permissions = _a.permissions;
320
- this.tokenData.permissions = permissions;
321
- delete originalConfig.headers['Authorization'];
322
- // In cookie mode the refresh reply renewed the access cookie, so retry without a header.
323
- if (!this.configuration.cookieAuth) {
324
- accessToken = "Bearer ".concat(tokenString);
325
- originalConfig.headers['Authorization'] = accessToken;
326
- this.configuration.accessToken = accessToken;
327
- this.tokenData.accessToken = tokenString;
328
- }
409
+ reply = _a.sent();
410
+ this.tokenData.permissions = reply.permissions;
411
+ this.applyTokenReply(reply);
329
412
  this.storeTokenData(this.tokenData);
330
- return [2 /*return*/, axios(originalConfig)];
413
+ if (!this.usesSession()) {
414
+ return [2 /*return*/, axios(this.withCurrentAuth(originalConfig))];
415
+ }
416
+ // Both mode: if the browser does not send the session cookie (another site), use the token.
417
+ return [2 /*return*/, axios(this.withCurrentAuth(originalConfig)).catch(function (retryError) {
418
+ var _a;
419
+ if (((_a = retryError === null || retryError === void 0 ? void 0 : retryError.response) === null || _a === void 0 ? void 0 : _a.status) !== 401 || !_this.tokenData.accessToken) {
420
+ return Promise.reject(retryError);
421
+ }
422
+ return _this.retryWithToken(axios, originalConfig);
423
+ })];
331
424
  case 3:
332
- _error_1 = _c.sent();
425
+ _error_1 = _a.sent();
333
426
  if (_error_1.response && _error_1.response.data) {
334
427
  return [2 /*return*/, Promise.reject(_error_1.response.data)];
335
428
  }
336
429
  return [2 /*return*/, Promise.reject(_error_1)];
337
- case 4: return [3 /*break*/, 9];
430
+ case 4: return [3 /*break*/, 10];
338
431
  case 5:
432
+ if (!(err.message === NETWORK_ERROR_MESSAGE && this.canRetryWithToken(originalConfig))) return [3 /*break*/, 6];
433
+ // Both mode: the browser blocked the session call or hid its reply; later calls use the token.
434
+ if (!this.canResend(originalConfig)) {
435
+ this.switchToToken();
436
+ return [2 /*return*/, Promise.reject(err)];
437
+ }
438
+ return [2 /*return*/, this.retryWithToken(axios, originalConfig)];
439
+ case 6:
339
440
  if (!(err.message === NETWORK_ERROR_MESSAGE
340
- && originalConfig.headers.hasOwnProperty('Authorization')
341
- && _retry_count < 4)) return [3 /*break*/, 9];
441
+ && (originalConfig.headers.hasOwnProperty('Authorization')
442
+ || (this.configuration.cookieAuth && originalConfig.withCredentials && this.canResend(originalConfig)))
443
+ && _retry_count < 4)) return [3 /*break*/, 10];
342
444
  _retry_count++;
343
- _c.label = 6;
344
- case 6:
345
- _c.trys.push([6, 8, , 9]);
346
- return [4 /*yield*/, this.refreshTokenInternal()];
445
+ _a.label = 7;
347
446
  case 7:
348
- _b = _c.sent(), tokenString = _b.accessToken, permissions = _b.permissions;
349
- accessToken = "Bearer ".concat(tokenString);
350
- this.tokenData.permissions = permissions;
447
+ _a.trys.push([7, 9, , 10]);
448
+ return [4 /*yield*/, this.refreshTokenInternal()];
449
+ case 8:
450
+ reply = _a.sent();
451
+ this.tokenData.permissions = reply.permissions;
351
452
  _retry = true;
352
- originalConfig.headers['Authorization'] = accessToken;
353
- this.configuration.accessToken = accessToken;
354
- this.tokenData.accessToken = tokenString;
453
+ this.applyTokenReply(reply);
355
454
  this.storeTokenData(this.tokenData);
356
- return [2 /*return*/, axios.request(__assign({}, originalConfig))];
357
- case 8:
358
- _error_2 = _c.sent();
455
+ return [2 /*return*/, axios.request(__assign({}, this.withCurrentAuth(originalConfig)))];
456
+ case 9:
457
+ _error_2 = _a.sent();
359
458
  if (_error_2.response && _error_2.response.data) {
360
459
  return [2 /*return*/, Promise.reject(_error_2.response.data)];
361
460
  }
362
461
  return [2 /*return*/, Promise.reject(_error_2)];
363
- case 9: return [2 /*return*/, Promise.reject(err)];
462
+ case 10: return [2 /*return*/, Promise.reject(err)];
364
463
  }
365
464
  });
366
465
  }); });
package/dist/common.d.ts CHANGED
@@ -62,7 +62,7 @@ export declare const toPathString: (url: URL) => string;
62
62
  *
63
63
  * @export
64
64
  */
65
- export declare const createRequestFunction: (axiosArgs: RequestArgs, globalAxios: AxiosInstance, BASE_PATH: string, configuration?: Configuration) => <T = unknown, R = AxiosResponse<T, any, {}>>(axios?: AxiosInstance, basePath?: string) => Promise<R>;
65
+ export declare const createRequestFunction: (axiosArgs: RequestArgs, globalAxios: AxiosInstance, BASE_PATH: string, configuration?: Configuration) => <T = unknown, R = AxiosResponse<T, any, {}, any>>(axios?: AxiosInstance, basePath?: string) => Promise<R>;
66
66
  /**
67
67
  * EMIL AccountService
68
68
  * The EMIL AccountService API description
package/dist/common.js CHANGED
@@ -233,6 +233,7 @@ exports.toPathString = toPathString;
233
233
  * @export
234
234
  */
235
235
  var createRequestFunction = function (axiosArgs, globalAxios, BASE_PATH, configuration) {
236
+ // Typed by hand: since axios 1.20, request() returns an internal axios type that SDK declarations cannot name.
236
237
  return function (axios, basePath) {
237
238
  if (axios === void 0) { axios = globalAxios; }
238
239
  if (basePath === void 0) { basePath = BASE_PATH; }
@@ -18,6 +18,7 @@ export interface ConfigurationParameters {
18
18
  baseOptions?: any;
19
19
  formDataCtor?: new () => any;
20
20
  cookieAuth?: boolean;
21
+ account?: string;
21
22
  }
22
23
  export declare class Configuration {
23
24
  /**
@@ -70,13 +71,20 @@ export declare class Configuration {
70
71
  */
71
72
  formDataCtor?: new () => any;
72
73
  /**
73
- * Authenticate with the HttpOnly access cookie instead of a stored token: every call sends credentials.
74
- * The browser holds one cookie, so use it with one logged-in account; log out through the auth service.
74
+ * Use a server session when authservice makes one; otherwise the token, as without this option.
75
+ * Set `account` too, so each SDK object keeps its own account.
75
76
  *
76
77
  * @type {boolean}
77
78
  * @memberof Configuration
78
79
  */
79
80
  cookieAuth?: boolean;
81
+ /**
82
+ * The session account (Cognito sub) this app shows, so the SDK never takes another tab's account.
83
+ *
84
+ * @type {string}
85
+ * @memberof Configuration
86
+ */
87
+ account?: string;
80
88
  constructor(param?: ConfigurationParameters);
81
89
  /**
82
90
  * Check if the given MIME is a JSON MIME.
@@ -25,6 +25,7 @@ var Configuration = /** @class */ (function () {
25
25
  this.baseOptions = param.baseOptions;
26
26
  this.formDataCtor = param.formDataCtor;
27
27
  this.cookieAuth = param.cookieAuth;
28
+ this.account = param.account;
28
29
  }
29
30
  /**
30
31
  * Check if the given MIME is a JSON MIME.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@emilgroup/account-sdk",
3
- "version": "1.49.1-beta.12",
3
+ "version": "1.49.1-beta.16",
4
4
  "description": "OpenAPI client for @emilgroup/account-sdk",
5
5
  "author": "OpenAPI-Generator Contributors",
6
6
  "keywords": [
@@ -18,7 +18,7 @@
18
18
  "prepare": "npm run build"
19
19
  },
20
20
  "dependencies": {
21
- "axios": "1.18.0"
21
+ "axios": "1.20.0"
22
22
  },
23
23
  "devDependencies": {
24
24