@thoughtspot/visual-embed-sdk 1.29.0-alpha.2 → 1.29.0-alpha.authRefactor

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.
Files changed (45) hide show
  1. package/cjs/package.json +1 -1
  2. package/cjs/src/embed/liveboard.js +1 -1
  3. package/cjs/src/embed/liveboard.js.map +1 -1
  4. package/cjs/src/utils/sessionInfoService.d.ts +66 -0
  5. package/cjs/src/utils/sessionInfoService.d.ts.map +1 -0
  6. package/cjs/src/utils/sessionInfoService.js +92 -0
  7. package/cjs/src/utils/sessionInfoService.js.map +1 -0
  8. package/dist/tsembed-react.es.js +2 -2
  9. package/dist/tsembed-react.js +2 -2
  10. package/dist/tsembed.es.js +2 -2
  11. package/dist/tsembed.js +2 -2
  12. package/lib/package.json +1 -1
  13. package/lib/src/embed/liveboard.js +1 -1
  14. package/lib/src/embed/liveboard.js.map +1 -1
  15. package/lib/src/utils/sessionInfoService.d.ts +66 -0
  16. package/lib/src/utils/sessionInfoService.d.ts.map +1 -0
  17. package/lib/src/utils/sessionInfoService.js +85 -0
  18. package/lib/src/utils/sessionInfoService.js.map +1 -0
  19. package/package.json +1 -2
  20. package/src/auth.spec.ts +66 -72
  21. package/src/auth.ts +43 -59
  22. package/src/authToken.ts +10 -4
  23. package/src/embed/app.spec.ts +4 -2
  24. package/src/embed/base.spec.ts +1 -0
  25. package/src/embed/base.ts +3 -0
  26. package/src/embed/embed.spec.ts +2 -0
  27. package/src/embed/events.spec.ts +2 -0
  28. package/src/embed/liveboard.spec.ts +2 -0
  29. package/src/embed/pinboard.spec.ts +2 -0
  30. package/src/embed/sage.spec.ts +3 -0
  31. package/src/embed/search.spec.ts +1 -0
  32. package/src/embed/ts-embed-trigger.spec.ts +3 -0
  33. package/src/embed/ts-embed.spec.ts +8 -0
  34. package/src/embed/ts-embed.ts +1 -0
  35. package/src/index.ts +2 -1
  36. package/src/mixpanel-service.spec.ts +4 -3
  37. package/src/mixpanel-service.ts +3 -1
  38. package/src/react/index.spec.tsx +7 -0
  39. package/src/react/index.tsx +1 -0
  40. package/src/utils/authService/authService.spec.ts +9 -4
  41. package/src/utils/authService/authService.ts +1 -0
  42. package/src/utils/authService/tokenizedAuthService.ts +38 -8
  43. package/src/utils/processData.spec.ts +3 -2
  44. package/src/utils/processData.ts +1 -3
  45. package/src/utils/sessionInfoService.ts +101 -0
@@ -0,0 +1,85 @@
1
+ import { getEmbedConfig } from '../embed/embedConfig';
2
+ import { fetchSessionInfoService } from './authService';
3
+ let sessionInfo = null;
4
+ /**
5
+ * Returns the session info object and caches it for future use.
6
+ * Once fetched the session info object is cached and returned from the cache on
7
+ * subsequent calls.
8
+ *
9
+ * @example ```js
10
+ * const sessionInfo = await getSessionInfo();
11
+ * console.log(sessionInfo);
12
+ * ```
13
+ * @version SDK: 1.28.3 | ThoughtSpot: *
14
+ * @returns {Promise<SessionInfo>} The session info object.
15
+ */
16
+ export async function getSessionInfo() {
17
+ if (!sessionInfo) {
18
+ const host = getEmbedConfig().thoughtSpotHost;
19
+ const sessionResponse = await fetchSessionInfoService(host);
20
+ const processedSessionInfo = getSessionDetails(sessionResponse);
21
+ sessionInfo = processedSessionInfo;
22
+ }
23
+ return sessionInfo;
24
+ }
25
+ /**
26
+ * Returns the cached session info object. If the client is not authenticated the
27
+ * function will return null.
28
+ *
29
+ * @example ```js
30
+ * const sessionInfo = getSessionInfoSync();
31
+ * if (sessionInfo) {
32
+ * console.log(sessionInfo);
33
+ * } else {
34
+ * console.log('Not authenticated');
35
+ * }
36
+ * ```
37
+ * @returns {SessionInfo | null} The session info object.
38
+ * @version SDK: 1.28.3 | ThoughtSpot: *
39
+ */
40
+ export function getSessionInfoSync() {
41
+ return sessionInfo;
42
+ }
43
+ /**
44
+ * Processes the session info response and returns the session info object.
45
+ *
46
+ * @param sessionInfoResp {any} Response from the session info API.
47
+ * @returns {SessionInfo} The session info object.
48
+ * @example ```js
49
+ * const sessionInfoResp = await fetch(sessionInfoPath);
50
+ * const sessionInfo = getSessionDetails(sessionInfoResp);
51
+ * console.log(sessionInfo);
52
+ * ```
53
+ * @version SDK: 1.28.3 | ThoughtSpot: *
54
+ */
55
+ export const getSessionDetails = (sessionInfoResp) => {
56
+ const devMixpanelToken = sessionInfoResp.configInfo.mixpanelConfig.devSdkKey;
57
+ const prodMixpanelToken = sessionInfoResp.configInfo.mixpanelConfig.prodSdkKey;
58
+ const mixpanelToken = sessionInfoResp.configInfo.mixpanelConfig.production
59
+ ? prodMixpanelToken
60
+ : devMixpanelToken;
61
+ return {
62
+ userGUID: sessionInfoResp.userGUID,
63
+ mixpanelToken,
64
+ isPublicUser: sessionInfoResp.configInfo.isPublicUser,
65
+ releaseVersion: sessionInfoResp.releaseVersion,
66
+ clusterId: sessionInfoResp.configInfo.selfClusterId,
67
+ clusterName: sessionInfoResp.configInfo.selfClusterName,
68
+ ...sessionInfoResp,
69
+ };
70
+ };
71
+ /**
72
+ * Resets the cached session info object and forces a new fetch on the next call.
73
+ *
74
+ * @example ```js
75
+ * resetCachedSessionInfo();
76
+ * const sessionInfo = await getSessionInfo();
77
+ * console.log(sessionInfo);
78
+ * ```
79
+ * @version SDK: 1.28.3 | ThoughtSpot ts7.april.cl, 7.2.1
80
+ * @returns {void}
81
+ */
82
+ export function resetCachedSessionInfo() {
83
+ sessionInfo = null;
84
+ }
85
+ //# sourceMappingURL=sessionInfoService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionInfoService.js","sourceRoot":"","sources":["../../../src/utils/sessionInfoService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAcxD,IAAI,WAAW,GAAuB,IAAI,CAAC;AAE3C;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc;IAChC,IAAI,CAAC,WAAW,EAAE;QACd,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC,eAAe,CAAC;QAC9C,MAAM,eAAe,GAAG,MAAM,uBAAuB,CAAC,IAAI,CAAC,CAAC;QAC5D,MAAM,oBAAoB,GAAG,iBAAiB,CAAC,eAAe,CAAC,CAAC;QAChE,WAAW,GAAG,oBAAoB,CAAC;KACtC;IACD,OAAO,WAAW,CAAC;AACvB,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,kBAAkB;IAC9B,OAAO,WAAW,CAAC;AACvB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,eAAoB,EAAe,EAAE;IACnE,MAAM,gBAAgB,GAAG,eAAe,CAAC,UAAU,CAAC,cAAc,CAAC,SAAS,CAAC;IAC7E,MAAM,iBAAiB,GAAG,eAAe,CAAC,UAAU,CAAC,cAAc,CAAC,UAAU,CAAC;IAC/E,MAAM,aAAa,GAAG,eAAe,CAAC,UAAU,CAAC,cAAc,CAAC,UAAU;QACtE,CAAC,CAAC,iBAAiB;QACnB,CAAC,CAAC,gBAAgB,CAAC;IACvB,OAAO;QACH,QAAQ,EAAE,eAAe,CAAC,QAAQ;QAClC,aAAa;QACb,YAAY,EAAE,eAAe,CAAC,UAAU,CAAC,YAAY;QACrD,cAAc,EAAE,eAAe,CAAC,cAAc;QAC9C,SAAS,EAAE,eAAe,CAAC,UAAU,CAAC,aAAa;QACnD,WAAW,EAAE,eAAe,CAAC,UAAU,CAAC,eAAe;QACvD,GAAG,eAAe;KACrB,CAAC;AACN,CAAC,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,UAAU,sBAAsB;IAClC,WAAW,GAAG,IAAI,CAAC;AACvB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thoughtspot/visual-embed-sdk",
3
- "version": "1.29.0-alpha.2",
3
+ "version": "1.29.0-alpha.authRefactor",
4
4
  "description": "ThoughtSpot Embed SDK",
5
5
  "module": "lib/src/index.js",
6
6
  "main": "dist/tsembed.js",
@@ -64,7 +64,6 @@
64
64
  "test": "npm run test-sdk && npm run test-docs",
65
65
  "posttest": "cat ./coverage/sdk/lcov.info | coveralls",
66
66
  "is-publish-allowed": "node scripts/is-publish-allowed.js",
67
- "prepublishOnly": "npm run is-publish-allowed && npm run test && npm run tsc && npm run bundle-dts-file && npm run bundle-dts && npm run bundle-dts-react && npm run bundle-dts-react-full && npm run build",
68
67
  "check-size": "npm run build && size-limit",
69
68
  "publish-dev": "npm publish --tag dev",
70
69
  "publish-prod": "npm publish --tag latest"
package/src/auth.spec.ts CHANGED
@@ -1,12 +1,13 @@
1
1
  import * as authInstance from './auth';
2
- import * as authService from './utils/authService/authService';
3
- import * as tokenAuthService from './utils/authService/tokenizedAuthService';
4
- import * as checkReleaseVersionInBetaInstance from './utils';
5
- import * as mixPanelService from './mixpanel-service';
2
+ import * as authTokenService from './authToken';
6
3
  import * as EmbedConfig from './embed/embedConfig';
7
- import { AuthType, EmbedEvent } from './types';
4
+ import * as mixPanelService from './mixpanel-service';
8
5
  import { executeAfterWait } from './test/test-utils';
9
- import { resetCachedAuthToken } from './authToken';
6
+ import { AuthType, EmbedEvent } from './types';
7
+ import * as checkReleaseVersionInBetaInstance from './utils';
8
+ import * as authService from './utils/authService/authService';
9
+ import * as tokenAuthService from './utils/authService/tokenizedAuthService';
10
+ import * as SessionService from './utils/sessionInfoService';
10
11
 
11
12
  const thoughtSpotHost = 'http://localhost:3000';
12
13
  const username = 'tsuser';
@@ -18,7 +19,7 @@ export const embedConfig: any = {
18
19
  thoughtSpotHost,
19
20
  username,
20
21
  authEndpoint: 'auth',
21
- authType: AuthType.AuthServer,
22
+ authType: AuthType.TrustedAuthToken,
22
23
  getAuthToken: jest.fn(() => Promise.resolve(token)),
23
24
  }),
24
25
  doTokenAuthWithCookieDetect: {
@@ -112,31 +113,47 @@ export const mockSessionInfo = {
112
113
  },
113
114
  };
114
115
 
116
+ export const mockSessionInfoApiResponse = {
117
+ userGUID: '1234',
118
+ releaseVersion: 'test',
119
+ configInfo: {
120
+ isPublicUser: false,
121
+ mixpanelConfig: {
122
+ production: true,
123
+ devSdkKey: 'devKey',
124
+ prodSdkKey: 'prodKey',
125
+ },
126
+ },
127
+ };
128
+
115
129
  describe('Unit test for auth', () => {
116
130
  beforeEach(() => {
131
+ jest.resetAllMocks();
117
132
  global.fetch = window.fetch;
118
133
  });
119
134
  afterEach(() => {
120
- resetCachedAuthToken();
135
+ authTokenService.resetCachedAuthToken();
136
+ SessionService.resetCachedSessionInfo();
121
137
  });
122
138
  test('endpoints, SAML_LOGIN_TEMPLATE', () => {
123
139
  const ssoTemplateUrl = authService.EndPoints.SAML_LOGIN_TEMPLATE(thoughtSpotHost);
124
140
  expect(ssoTemplateUrl).toBe(`/callosum/v1/saml/login?targetURLPath=${thoughtSpotHost}`);
125
141
  });
126
142
 
127
- test('when session info giving response', async () => {
128
- jest.spyOn(mixPanelService, 'initMixpanel').mockImplementation(() => Promise.resolve());
129
- authInstance.initSession(mockSessionInfo);
130
- const sessionInfo = await authInstance.getSessionInfo();
131
- expect(sessionInfo).toStrictEqual(mockSessionInfo);
143
+ test('when session info giving response, it is cached', async () => {
144
+ jest.spyOn(tokenAuthService, 'fetchSessionInfoService').mockResolvedValueOnce(mockSessionInfoApiResponse);
145
+ const sessionInfo = await SessionService.getSessionInfo();
146
+ expect(sessionInfo.mixpanelToken).toEqual('prodKey');
147
+ expect(sessionInfo.isPublicUser).toEqual(false);
148
+ await SessionService.getSessionInfo();
149
+ expect(tokenAuthService.fetchSessionInfoService).toHaveBeenCalledTimes(1);
132
150
  });
133
151
 
134
152
  test('Disable mixpanel when disableSDKTracking flag is set', () => {
135
- jest.clearAllMocks();
136
- jest.resetAllMocks();
137
153
  jest.spyOn(mixPanelService, 'initMixpanel');
154
+ jest.spyOn(SessionService, 'getSessionInfo').mockReturnValue(mockSessionInfo);
138
155
  jest.spyOn(EmbedConfig, 'getEmbedConfig').mockReturnValue({ disableSDKTracking: true });
139
- authInstance.initSession(mockSessionInfo);
156
+ authInstance.postLoginService();
140
157
  expect(mixPanelService.initMixpanel).not.toBeCalled();
141
158
  });
142
159
 
@@ -163,28 +180,22 @@ describe('Unit test for auth', () => {
163
180
  });
164
181
 
165
182
  test('doTokenAuth: when user is loggedIn', async () => {
166
- jest.spyOn(tokenAuthService, 'fetchSessionInfoService').mockImplementation(async () => ({
167
- json: () => mockSessionInfo,
168
- status: 200,
169
- }));
170
- jest.spyOn(authInstance, 'getSessionDetails').mockReturnValue(mockSessionInfo);
171
- jest.spyOn(authInstance, 'initSession').mockReturnValue(null);
183
+ const getAuthenticationTokenMock = jest.spyOn(authTokenService, 'getAuthenticationToken');
184
+ jest.spyOn(tokenAuthService, 'isActiveService').mockImplementation(async () => true);
172
185
  await authInstance.doTokenAuth(embedConfig.doTokenAuthSuccess('authToken'));
173
- expect(tokenAuthService.fetchSessionInfoService).toBeCalled();
186
+ expect(authTokenService.getAuthenticationToken).not.toBeCalled();
174
187
  expect(authInstance.loggedInStatus).toBe(true);
188
+ getAuthenticationTokenMock.mockRestore();
175
189
  });
176
190
 
177
191
  test('doTokenAuth: when user is not loggedIn & getAuthToken have response', async () => {
178
- jest.spyOn(tokenAuthService, 'fetchSessionInfoService').mockImplementation(() => false);
179
- jest.spyOn(authService, 'fetchAuthTokenService').mockImplementation(() => ({
180
- text: () => Promise.resolve('abc'),
181
- }));
192
+ jest.spyOn(tokenAuthService, 'isActiveService').mockImplementation(async () => false);
182
193
  jest.spyOn(authService, 'fetchAuthService').mockImplementation(() => Promise.resolve({
183
194
  status: 200,
195
+ ok: true,
184
196
  }));
185
197
  jest.spyOn(authService, 'verifyTokenService').mockResolvedValueOnce(true);
186
198
  await authInstance.doTokenAuth(embedConfig.doTokenAuthSuccess('authToken2'));
187
- expect(tokenAuthService.fetchSessionInfoService).toBeCalled();
188
199
  expect(authService.fetchAuthService).toBeCalledWith(
189
200
  thoughtSpotHost,
190
201
  username,
@@ -193,26 +204,25 @@ describe('Unit test for auth', () => {
193
204
  });
194
205
 
195
206
  test('doTokenAuth: when user is not loggedIn & getAuthToken not present, isLoggedIn should called', async () => {
196
- jest.spyOn(tokenAuthService, 'fetchSessionInfoService').mockImplementation(() => false);
197
- jest.spyOn(authService, 'fetchAuthTokenService').mockImplementation(() => Promise.resolve({ text: () => Promise.resolve('abc') }));
207
+ jest.spyOn(tokenAuthService, 'isActiveService').mockImplementation(async () => false);
208
+ jest.spyOn(authService, 'fetchAuthTokenService').mockImplementation(() => ({
209
+ text: () => Promise.resolve('abc'),
210
+ }));
198
211
  jest.spyOn(authService, 'fetchAuthService').mockImplementation(() => Promise.resolve({
199
212
  status: 200,
200
213
  ok: true,
201
214
  }));
202
215
  jest.spyOn(authService, 'verifyTokenService').mockResolvedValueOnce(true);
203
216
  await authInstance.doTokenAuth(embedConfig.doTokenAuthFailureWithoutGetAuthToken);
217
+ expect(authService.fetchAuthTokenService).toBeCalledWith('auth');
204
218
  await executeAfterWait(() => {
205
219
  expect(authInstance.loggedInStatus).toBe(true);
206
- expect(tokenAuthService.fetchSessionInfoService).toBeCalled();
207
- expect(authService.fetchAuthService).toBeCalledWith(
208
- thoughtSpotHost,
209
- username,
210
- 'authToken2',
211
- );
220
+ expect(authService.fetchAuthService).toBeCalledWith(thoughtSpotHost, username, 'abc');
212
221
  });
213
222
  });
214
223
 
215
224
  test('doTokenAuth: Should raise error when duplicate token is used', async () => {
225
+ jest.spyOn(tokenAuthService, 'isActiveService').mockImplementation(async () => false);
216
226
  jest.spyOn(tokenAuthService, 'fetchSessionInfoService').mockResolvedValue({
217
227
  status: 401,
218
228
  });
@@ -254,8 +264,10 @@ describe('Unit test for auth', () => {
254
264
  ok: true,
255
265
  }));
256
266
  jest.spyOn(authService, 'verifyTokenService').mockResolvedValueOnce(true);
267
+ jest.spyOn(tokenAuthService, 'isActiveService').mockResolvedValueOnce(false);
268
+ jest.spyOn(tokenAuthService, 'isActiveService').mockResolvedValueOnce(false);
257
269
  const isLoggedIn = await authInstance.doTokenAuth(embedConfig.doTokenAuthWithCookieDetect);
258
- expect(tokenAuthService.fetchSessionInfoService).toHaveBeenCalledTimes(2);
270
+ expect(tokenAuthService.isActiveService).toHaveBeenCalledTimes(2);
259
271
  expect(isLoggedIn).toBe(false);
260
272
  });
261
273
 
@@ -278,7 +290,6 @@ describe('Unit test for auth', () => {
278
290
  expect(await authInstance.doTokenAuth(embedConfig.doTokenAuthSuccess('authToken2'))).toBe(
279
291
  true,
280
292
  );
281
- expect(tokenAuthService.fetchSessionInfoService).toBeCalled();
282
293
  expect(authService.fetchAuthPostService).toBeCalledWith(
283
294
  thoughtSpotHost,
284
295
  username,
@@ -297,20 +308,9 @@ describe('Unit test for auth', () => {
297
308
  });
298
309
 
299
310
  it('when user is loggedIn', async () => {
300
- spyOn(checkReleaseVersionInBetaInstance, 'checkReleaseVersionInBeta');
301
- jest.spyOn(authInstance, 'getSessionDetails').mockReturnValue(mockSessionInfo);
302
- jest.spyOn(authInstance, 'initSession').mockReturnValue(null);
303
- jest.spyOn(tokenAuthService, 'fetchSessionInfoService').mockImplementation(
304
- async () => ({
305
- json: () => mockSessionInfo,
306
- status: 200,
307
- }),
308
- );
311
+ jest.spyOn(tokenAuthService, 'isActiveService').mockResolvedValueOnce(true);
309
312
  await authInstance.doBasicAuth(embedConfig.doBasicAuth);
310
- expect(tokenAuthService.fetchSessionInfoService).toBeCalled();
311
313
  expect(authInstance.loggedInStatus).toBe(true);
312
- expect(authInstance.getSessionDetails).toBeCalled();
313
- expect(authInstance.initSession).toBeCalled();
314
314
  });
315
315
 
316
316
  it('when user is not loggedIn', async () => {
@@ -321,7 +321,7 @@ describe('Unit test for auth', () => {
321
321
  }));
322
322
 
323
323
  await authInstance.doBasicAuth(embedConfig.doBasicAuth);
324
- expect(tokenAuthService.fetchSessionInfoService).toBeCalled();
324
+ // expect(tokenAuthService.fetchSessionInfoService).toBeCalled();
325
325
  expect(authService.fetchBasicAuthService).toBeCalled();
326
326
  expect(authInstance.loggedInStatus).toBe(true);
327
327
  });
@@ -349,10 +349,8 @@ describe('Unit test for auth', () => {
349
349
  status: 200,
350
350
  }),
351
351
  );
352
- jest.spyOn(authInstance, 'getSessionDetails').mockReturnValue(mockSessionInfo);
353
- jest.spyOn(authInstance, 'initSession').mockReturnValue(null);
352
+ jest.spyOn(tokenAuthService, 'isActiveService').mockReturnValue(true);
354
353
  await authInstance.doSamlAuth(embedConfig.doSamlAuth);
355
- expect(tokenAuthService.fetchSessionInfoService).toBeCalled();
356
354
  expect(window.location.hash).toBe('');
357
355
  expect(authInstance.loggedInStatus).toBe(true);
358
356
  });
@@ -360,7 +358,6 @@ describe('Unit test for auth', () => {
360
358
  it('when user is not loggedIn & isAtSSORedirectUrl is true', async () => {
361
359
  jest.spyOn(tokenAuthService, 'fetchSessionInfoService').mockImplementation(() => Promise.reject());
362
360
  await authInstance.doSamlAuth(embedConfig.doSamlAuth);
363
- expect(tokenAuthService.fetchSessionInfoService).toBeCalled();
364
361
  expect(window.location.hash).toBe('');
365
362
  expect(authInstance.loggedInStatus).toBe(false);
366
363
  });
@@ -374,7 +371,6 @@ describe('Unit test for auth', () => {
374
371
  });
375
372
  jest.spyOn(tokenAuthService, 'fetchSessionInfoService').mockImplementation(() => Promise.reject());
376
373
  await authInstance.doSamlAuth(embedConfig.doSamlAuth);
377
- expect(tokenAuthService.fetchSessionInfoService).toBeCalled();
378
374
  expect(global.window.location.href).toBe(samalLoginUrl);
379
375
  });
380
376
 
@@ -387,14 +383,9 @@ describe('Unit test for auth', () => {
387
383
  });
388
384
  spyOn(authInstance, 'samlCompletionPromise');
389
385
  global.window.open = jest.fn();
390
- jest.spyOn(tokenAuthService, 'fetchSessionInfoService')
391
- .mockImplementationOnce(() => Promise.reject())
392
- .mockImplementationOnce(async () => ({
393
- json: () => mockSessionInfo,
394
- status: 200,
395
- }));
396
- jest.spyOn(authInstance, 'getSessionDetails').mockReturnValue(mockSessionInfo);
397
- jest.spyOn(authInstance, 'initSession').mockReturnValue(null);
386
+ jest.spyOn(tokenAuthService, 'isActiveService')
387
+ .mockReturnValueOnce(false)
388
+ .mockReturnValueOnce(true);
398
389
  expect(await authInstance.samlCompletionPromise).not.toBe(null);
399
390
  expect(
400
391
  await authInstance.doSamlAuth({
@@ -404,15 +395,13 @@ describe('Unit test for auth', () => {
404
395
  document.getElementById('ts-auth-btn').click();
405
396
  window.postMessage({ type: EmbedEvent.SAMLComplete }, '*');
406
397
  await authInstance.samlCompletionPromise;
407
- expect(tokenAuthService.fetchSessionInfoService).toBeCalled();
408
- expect(authInstance.getSessionDetails).toBeCalled();
409
- expect(authInstance.initSession).toBeCalled();
398
+ expect(authInstance.loggedInStatus).toBe(true);
410
399
  });
411
400
  });
412
401
 
413
402
  describe('doOIDCAuth', () => {
414
403
  afterEach(() => {
415
- resetCachedAuthToken();
404
+ authTokenService.resetCachedAuthToken();
416
405
  delete global.window;
417
406
  global.window = Object.create(originalWindow);
418
407
  global.window.open = jest.fn();
@@ -422,7 +411,6 @@ describe('Unit test for auth', () => {
422
411
  it('when user is not loggedIn & isAtSSORedirectUrl is true', async () => {
423
412
  jest.spyOn(tokenAuthService, 'fetchSessionInfoService').mockImplementation(() => Promise.reject());
424
413
  await authInstance.doOIDCAuth(embedConfig.doOidcAuth);
425
- expect(tokenAuthService.fetchSessionInfoService).toBeCalled();
426
414
  expect(window.location.hash).toBe('');
427
415
  expect(authInstance.loggedInStatus).toBe(false);
428
416
  });
@@ -465,6 +453,7 @@ describe('Unit test for auth', () => {
465
453
 
466
454
  it('authenticate: when authType is Basic', async () => {
467
455
  jest.spyOn(authInstance, 'doBasicAuth');
456
+ jest.spyOn(authService, 'fetchBasicAuthService').mockImplementation(() => Promise.resolve({ status: 200, ok: true }));
468
457
  await authInstance.authenticate(embedConfig.basicAuthSuccess);
469
458
  expect(authInstance.doBasicAuth).toBeCalled();
470
459
  expect(authInstance.loggedInStatus).toBe(true);
@@ -481,6 +470,7 @@ describe('Unit test for auth', () => {
481
470
  });
482
471
 
483
472
  it('doCookielessTokenAuth should resolve to true if valid token is passed', async () => {
473
+ jest.clearAllMocks();
484
474
  jest.spyOn(authService, 'verifyTokenService').mockResolvedValueOnce(true);
485
475
  const isLoggedIn = await authInstance.doCookielessTokenAuth(
486
476
  embedConfig.doCookielessAuth('testToken'),
@@ -500,11 +490,11 @@ describe('Unit test for auth', () => {
500
490
  authInstance.setAuthEE(testObject as any);
501
491
  expect(authInstance.getAuthEE()).toBe(testObject);
502
492
  });
503
- it('getSessionDetails returns the correct details given sessionInfo', () => {
493
+ it('getSessionDetails returns the correct details given sessionInfo', async () => {
504
494
  jest.clearAllMocks();
505
495
  jest.restoreAllMocks();
506
496
 
507
- const details = authInstance.getSessionDetails({
497
+ jest.spyOn(tokenAuthService, 'fetchSessionInfoService').mockReturnValue({
508
498
  userGUID: '1234',
509
499
  releaseVersion: '1',
510
500
  configInfo: {
@@ -515,13 +505,14 @@ describe('Unit test for auth', () => {
515
505
  },
516
506
  },
517
507
  });
508
+ const details = await SessionService.getSessionInfo();
518
509
  expect(details).toEqual(
519
510
  expect.objectContaining({
520
511
  mixpanelToken: 'devKey',
521
512
  }),
522
513
  );
523
514
 
524
- const details2 = authInstance.getSessionDetails({
515
+ jest.spyOn(tokenAuthService, 'fetchSessionInfoService').mockReturnValue({
525
516
  configInfo: {
526
517
  mixpanelConfig: {
527
518
  devSdkKey: 'devKey',
@@ -530,6 +521,9 @@ describe('Unit test for auth', () => {
530
521
  },
531
522
  },
532
523
  });
524
+
525
+ SessionService.resetCachedSessionInfo();
526
+ const details2 = await SessionService.getSessionInfo();
533
527
  expect(details2).toEqual(
534
528
  expect.objectContaining({
535
529
  mixpanelToken: 'prodKey',
package/src/auth.ts CHANGED
@@ -1,20 +1,21 @@
1
1
  import EventEmitter from 'eventemitter3';
2
+ import { getAuthenticationToken, resetCachedAuthToken } from './authToken';
3
+ import { getEmbedConfig } from './embed/embedConfig';
2
4
  import { initMixpanel } from './mixpanel-service';
3
5
  import {
4
- AuthType, DOMSelector, EmbedConfig, EmbedEvent, Param,
6
+ AuthType, DOMSelector, EmbedConfig, EmbedEvent,
5
7
  } from './types';
6
8
  import { getDOMNode, getRedirectUrl } from './utils';
7
9
  import {
8
- fetchSessionInfoService,
10
+ EndPoints,
11
+ fetchAuthPostService,
9
12
  fetchAuthService,
10
13
  fetchBasicAuthService,
11
14
  fetchLogoutService,
12
- fetchAuthPostService,
13
- EndPoints,
14
15
  } from './utils/authService';
15
- import { getAuthenticationToken, resetCachedAuthToken } from './authToken';
16
+ import { isActiveService } from './utils/authService/tokenizedAuthService';
16
17
  import { logger } from './utils/logger';
17
- import { getEmbedConfig } from './embed/embedConfig';
18
+ import { getSessionInfo, getSessionInfoSync } from './utils/sessionInfoService';
18
19
 
19
20
  // eslint-disable-next-line import/no-mutable-exports
20
21
  export let loggedInStatus = false;
@@ -22,11 +23,7 @@ export let loggedInStatus = false;
22
23
  export let samlAuthWindow: Window = null;
23
24
  // eslint-disable-next-line import/no-mutable-exports
24
25
  export let samlCompletionPromise: Promise<void> = null;
25
- let sessionInfo: sessionInfoInterface = null;
26
- let sessionInfoResolver: (value: sessionInfoInterface) => void = null;
27
- const sessionInfoPromise = new Promise((resolve: (value: sessionInfoInterface) => void) => {
28
- sessionInfoResolver = resolve;
29
- });
26
+
30
27
  let releaseVersion = '';
31
28
 
32
29
  export const SSO_REDIRECTION_MARKER_GUID = '5e16222e-ef02-43e9-9fbd-24226bf3ce5b';
@@ -185,6 +182,10 @@ export function notifyAuthSuccess(): void {
185
182
  logger.error('SDK not initialized');
186
183
  return;
187
184
  }
185
+ const sessionInfo = getSessionInfoSync();
186
+ if (!sessionInfo) {
187
+ logger.error('Session info not available');
188
+ }
188
189
  authEE.emit(AuthStatus.SUCCESS, sessionInfo);
189
190
  }
190
191
 
@@ -211,70 +212,46 @@ export function notifyLogout(): void {
211
212
  authEE.emit(AuthStatus.LOGOUT);
212
213
  }
213
214
 
214
- export const initSession = (sessionDetails: sessionInfoInterface) => {
215
- const embedConfig = getEmbedConfig();
216
- if (sessionInfo == null) {
217
- sessionInfo = sessionDetails;
218
- if (!embedConfig.disableSDKTracking) {
219
- initMixpanel(sessionInfo);
220
- }
221
- sessionInfoResolver(sessionInfo);
222
- }
223
- };
224
-
225
- export const getSessionDetails = (sessionInfoResp: any): sessionInfoInterface => {
226
- const devMixpanelToken = sessionInfoResp.configInfo.mixpanelConfig.devSdkKey;
227
- const prodMixpanelToken = sessionInfoResp.configInfo.mixpanelConfig.prodSdkKey;
228
- const mixpanelToken = sessionInfoResp.configInfo.mixpanelConfig.production
229
- ? prodMixpanelToken
230
- : devMixpanelToken;
231
- return {
232
- userGUID: sessionInfoResp.userGUID,
233
- mixpanelToken,
234
- isPublicUser: sessionInfoResp.configInfo.isPublicUser,
235
- releaseVersion: sessionInfoResp.releaseVersion,
236
- clusterId: sessionInfoResp.configInfo.selfClusterId,
237
- clusterName: sessionInfoResp.configInfo.selfClusterName,
238
- ...sessionInfoResp,
239
- };
240
- };
241
-
242
215
  /**
243
216
  * Check if we are logged into the ThoughtSpot cluster
244
217
  *
245
218
  * @param thoughtSpotHost The ThoughtSpot cluster hostname or IP
246
219
  */
247
220
  async function isLoggedIn(thoughtSpotHost: string): Promise<boolean> {
248
- const authVerificationUrl = `${thoughtSpotHost}${EndPoints.AUTH_VERIFICATION}`;
249
- let response = null;
250
221
  try {
251
- response = await fetchSessionInfoService(authVerificationUrl);
252
- const sessionInfoResp = await response.json();
253
- const sessionDetails = getSessionDetails(sessionInfoResp);
254
- // Store user session details from session info
255
- initSession(sessionDetails);
256
- releaseVersion = sessionInfoResp.releaseVersion;
222
+ const response = await isActiveService(thoughtSpotHost);
223
+ return response;
257
224
  } catch (e) {
258
225
  return false;
259
226
  }
260
- return response.status === 200;
261
227
  }
262
228
 
263
229
  /**
264
- * Return releaseVersion if available
230
+ * Services to be called after the login is successful,
231
+ * This should be called after the cookie is set for cookie auth or
232
+ * after the token is set for cookieless.
233
+ *
234
+ * @return {Promise<void>}
235
+ * @example
236
+ * ```js
237
+ * await postLoginService();
238
+ * ```
239
+ * @version SDK: 1.28.3 | ThoughtSpot: *
265
240
  */
266
- export function getReleaseVersion() {
267
- return releaseVersion;
241
+ export async function postLoginService(): Promise<void> {
242
+ const sessionInfo = await getSessionInfo();
243
+ releaseVersion = sessionInfo.releaseVersion;
244
+ const embedConfig = getEmbedConfig();
245
+ if (!embedConfig.disableSDKTracking) {
246
+ initMixpanel(sessionInfo);
247
+ }
268
248
  }
269
249
 
270
250
  /**
271
- * Return a promise that resolves with the session information when
272
- * authentication is successful. And info is available.
273
- *
274
- * @group Global methods
251
+ * Return releaseVersion if available
275
252
  */
276
- export function getSessionInfo(): Promise<sessionInfoInterface> {
277
- return sessionInfoPromise;
253
+ export function getReleaseVersion() {
254
+ return releaseVersion;
278
255
  }
279
256
 
280
257
  /**
@@ -309,8 +286,15 @@ export const doTokenAuth = async (embedConfig: EmbedConfig): Promise<boolean> =>
309
286
  throw new Error('Either auth endpoint or getAuthToken function must be provided');
310
287
  }
311
288
  loggedInStatus = await isLoggedIn(thoughtSpotHost);
289
+
312
290
  if (!loggedInStatus) {
313
- const authToken = await getAuthenticationToken(embedConfig);
291
+ let authToken: string;
292
+ try {
293
+ authToken = await getAuthenticationToken(embedConfig);
294
+ } catch (e) {
295
+ loggedInStatus = false;
296
+ throw e;
297
+ }
314
298
  let resp;
315
299
  try {
316
300
  resp = await fetchAuthPostService(thoughtSpotHost, username, authToken);
@@ -492,7 +476,7 @@ export const logout = async (embedConfig: EmbedConfig): Promise<boolean> => {
492
476
  const { thoughtSpotHost } = embedConfig;
493
477
  await fetchLogoutService(thoughtSpotHost);
494
478
  resetCachedAuthToken();
495
- const thoughtspotIframes = document.querySelectorAll('[data-ts-iframe=\'true\']');
479
+ const thoughtspotIframes = document.querySelectorAll("[data-ts-iframe='true']");
496
480
  if (thoughtspotIframes?.length) {
497
481
  thoughtspotIframes.forEach((el) => {
498
482
  el.parentElement.innerHTML = embedConfig.loginFailedMessage;
package/src/authToken.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { EmbedConfig } from './types';
2
2
  import { fetchAuthTokenService, verifyTokenService } from './utils/authService/authService';
3
+ import { logger } from './utils/logger';
3
4
 
4
5
  const DUPLICATE_TOKEN_ERR = 'Duplicate token, please issue a new token every time getAuthToken callback is called.'
5
6
  + 'See https://developers.thoughtspot.com/docs/?pageid=embed-auth#trusted-auth-embed for more details.';
@@ -9,7 +10,7 @@ const INVALID_TOKEN_ERR = 'Invalid token received form token callback or authTok
9
10
  let cachedAuthToken: string | null = null;
10
11
 
11
12
  // This method can be used to get the authToken using the embedConfig
12
- export const getAuthenticationToken = async (embedConfig: EmbedConfig): Promise<string> => {
13
+ export async function getAuthenticationToken(embedConfig: EmbedConfig): Promise<string> {
13
14
  if (cachedAuthToken) {
14
15
  let isCachedTokenStillValid;
15
16
  try {
@@ -31,12 +32,17 @@ export const getAuthenticationToken = async (embedConfig: EmbedConfig): Promise<
31
32
  authToken = await response.text();
32
33
  }
33
34
 
34
- // this will throw error if the token is not valid
35
- await validateAuthToken(embedConfig, authToken);
35
+ try {
36
+ // this will throw error if the token is not valid
37
+ await validateAuthToken(embedConfig, authToken);
38
+ } catch (e) {
39
+ logger.error(`Received invalid token from getAuthToken callback or authToken endpoint. Error : ${e.message}`);
40
+ throw e;
41
+ }
36
42
 
37
43
  cachedAuthToken = authToken;
38
44
  return authToken;
39
- };
45
+ }
40
46
 
41
47
  const validateAuthToken = async (
42
48
  embedConfig: EmbedConfig,