@asgardeo/auth-spa 0.2.17 → 0.2.20

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 (115) hide show
  1. package/.eslintrc.js +1 -2
  2. package/README.md +12 -5
  3. package/babel.config.js +1 -2
  4. package/dist/asgardeo-spa.production.esm.js +100 -42
  5. package/dist/asgardeo-spa.production.esm.js.map +1 -1
  6. package/dist/asgardeo-spa.production.js +101 -43
  7. package/dist/asgardeo-spa.production.js.map +1 -1
  8. package/dist/asgardeo-spa.production.min.js +1 -1
  9. package/dist/asgardeo-spa.production.min.js.map +1 -1
  10. package/dist/polyfilled/asgardeo-spa.production.esm.js +98 -40
  11. package/dist/polyfilled/asgardeo-spa.production.esm.js.map +1 -1
  12. package/dist/polyfilled/asgardeo-spa.production.js +98 -40
  13. package/dist/polyfilled/asgardeo-spa.production.js.map +1 -1
  14. package/dist/polyfilled/asgardeo-spa.production.min.js +1 -1
  15. package/dist/polyfilled/asgardeo-spa.production.min.js.map +1 -1
  16. package/dist/src/client.d.ts +3 -2
  17. package/dist/src/client.d.ts.map +1 -1
  18. package/dist/src/client.js +13 -4
  19. package/dist/src/client.js.map +1 -1
  20. package/dist/src/clients/main-thread-client.d.ts.map +1 -1
  21. package/dist/src/clients/main-thread-client.js +41 -25
  22. package/dist/src/clients/main-thread-client.js.map +1 -1
  23. package/dist/src/clients/web-worker-client.d.ts +1 -1
  24. package/dist/src/clients/web-worker-client.d.ts.map +1 -1
  25. package/dist/src/clients/web-worker-client.js +55 -30
  26. package/dist/src/clients/web-worker-client.js.map +1 -1
  27. package/dist/src/constants/errors.d.ts +19 -0
  28. package/dist/src/constants/errors.d.ts.map +1 -0
  29. package/dist/src/constants/errors.js +19 -0
  30. package/dist/src/constants/errors.js.map +1 -0
  31. package/dist/src/constants/hooks.d.ts +2 -1
  32. package/dist/src/constants/hooks.d.ts.map +1 -1
  33. package/dist/src/constants/hooks.js +1 -0
  34. package/dist/src/constants/hooks.js.map +1 -1
  35. package/dist/src/constants/index.d.ts +1 -0
  36. package/dist/src/constants/index.d.ts.map +1 -1
  37. package/dist/src/constants/index.js +1 -0
  38. package/dist/src/constants/index.js.map +1 -1
  39. package/dist/src/constants/parameters.d.ts +1 -0
  40. package/dist/src/constants/parameters.d.ts.map +1 -1
  41. package/dist/src/constants/parameters.js +1 -0
  42. package/dist/src/constants/parameters.js.map +1 -1
  43. package/dist/src/helpers/session-management-helper.d.ts +1 -1
  44. package/dist/src/helpers/session-management-helper.d.ts.map +1 -1
  45. package/dist/src/helpers/session-management-helper.js +33 -43
  46. package/dist/src/helpers/session-management-helper.js.map +1 -1
  47. package/dist/src/index-polyfill.d.ts +0 -1
  48. package/dist/src/index-polyfill.d.ts.map +1 -1
  49. package/dist/src/index-polyfill.js +0 -1
  50. package/dist/src/index-polyfill.js.map +1 -1
  51. package/dist/src/models/client.d.ts +3 -3
  52. package/dist/src/models/client.d.ts.map +1 -1
  53. package/dist/src/models/http-client.d.ts +1 -1
  54. package/dist/src/models/http-client.d.ts.map +1 -1
  55. package/dist/src/models/index.d.ts +1 -0
  56. package/dist/src/models/index.d.ts.map +1 -1
  57. package/dist/src/models/index.js +1 -0
  58. package/dist/src/models/index.js.map +1 -1
  59. package/dist/src/models/message.d.ts +1 -0
  60. package/dist/src/models/message.d.ts.map +1 -1
  61. package/dist/src/models/request-custom-grant.d.ts +25 -0
  62. package/dist/src/models/request-custom-grant.d.ts.map +1 -0
  63. package/dist/src/models/request-custom-grant.js +19 -0
  64. package/dist/src/models/request-custom-grant.js.map +1 -0
  65. package/dist/src/models/session-management-helper.d.ts +2 -1
  66. package/dist/src/models/session-management-helper.d.ts.map +1 -1
  67. package/dist/src/models/sign-out-error.d.ts +22 -0
  68. package/dist/src/models/sign-out-error.d.ts.map +1 -0
  69. package/dist/src/models/sign-out-error.js +19 -0
  70. package/dist/src/models/sign-out-error.js.map +1 -0
  71. package/dist/src/models/web-worker.d.ts +3 -3
  72. package/dist/src/models/web-worker.d.ts.map +1 -1
  73. package/dist/src/models/web-worker.js.map +1 -1
  74. package/dist/src/public-api.d.ts +1 -1
  75. package/dist/src/public-api.d.ts.map +1 -1
  76. package/dist/src/public-api.js +1 -1
  77. package/dist/src/public-api.js.map +1 -1
  78. package/dist/src/utils/crypto-utils.d.ts +35 -0
  79. package/dist/src/utils/crypto-utils.d.ts.map +1 -0
  80. package/dist/src/utils/crypto-utils.js +58 -0
  81. package/dist/src/utils/crypto-utils.js.map +1 -0
  82. package/dist/src/utils/spa-utils.d.ts +5 -3
  83. package/dist/src/utils/spa-utils.d.ts.map +1 -1
  84. package/dist/src/utils/spa-utils.js +26 -10
  85. package/dist/src/utils/spa-utils.js.map +1 -1
  86. package/dist/src/worker/client.worker.js +2 -2
  87. package/dist/src/worker/client.worker.js.map +1 -1
  88. package/dist/src/worker/worker-core.d.ts.map +1 -1
  89. package/dist/src/worker/worker-core.js +26 -26
  90. package/dist/src/worker/worker-core.js.map +1 -1
  91. package/dist/tsconfig.tsbuildinfo +1 -1
  92. package/package.json +31 -30
  93. package/rollup.config.js +3 -5
  94. package/src/client.ts +22 -8
  95. package/src/clients/main-thread-client.ts +52 -21
  96. package/src/clients/web-worker-client.ts +67 -26
  97. package/src/constants/errors.ts +19 -0
  98. package/src/constants/hooks.ts +2 -1
  99. package/src/constants/index.ts +1 -0
  100. package/src/constants/parameters.ts +1 -0
  101. package/src/helpers/session-management-helper.ts +31 -40
  102. package/src/index-polyfill.ts +0 -1
  103. package/src/models/client.ts +3 -2
  104. package/src/models/http-client.ts +3 -2
  105. package/src/models/index.ts +1 -0
  106. package/src/models/message.ts +1 -0
  107. package/src/models/request-custom-grant.ts +26 -0
  108. package/src/models/session-management-helper.ts +3 -2
  109. package/src/models/sign-out-error.ts +22 -0
  110. package/src/models/web-worker.ts +8 -2
  111. package/src/public-api.ts +2 -1
  112. package/src/utils/crypto-utils.ts +78 -0
  113. package/src/utils/spa-utils.ts +33 -10
  114. package/src/worker/client.worker.ts +1 -1
  115. package/src/worker/worker-core.ts +29 -27
@@ -16,7 +16,7 @@
16
16
  * under the License.
17
17
  */
18
18
 
19
- import { SESSION_STATE } from "@asgardeo/auth-js";
19
+ import { GetAuthURLConfig, SESSION_STATE } from "@asgardeo/auth-js";
20
20
  import {
21
21
  CHECK_SESSION_SIGNED_IN,
22
22
  CHECK_SESSION_SIGNED_OUT,
@@ -27,6 +27,7 @@ import {
27
27
  SET_SESSION_STATE_FROM_IFRAME,
28
28
  SILENT_SIGN_IN_STATE,
29
29
  STATE,
30
+ STATE_QUERY,
30
31
  Storage
31
32
  } from "../constants";
32
33
  import { AuthorizationInfo, Message, SessionManagementHelperInterface } from "../models";
@@ -38,14 +39,13 @@ export const SessionManagementHelper = (() => {
38
39
  let _sessionState: () => Promise<string>;
39
40
  let _interval: number;
40
41
  let _redirectURL: string;
41
- let _authorizationEndpoint: string;
42
42
  let _sessionRefreshInterval: number;
43
43
  let _signOut: () => Promise<string>;
44
44
  let _sessionRefreshIntervalTimeout: number;
45
45
  let _checkSessionIntervalTimeout: number;
46
46
  let _storage: Storage;
47
47
  let _setSessionState: (sessionState: string) => void;
48
- let _isPKCEEnabled: boolean;
48
+ let _getAuthorizationURL: (params?: GetAuthURLConfig) => Promise<string>;
49
49
 
50
50
  const initialize = (
51
51
  clientID: string,
@@ -54,17 +54,15 @@ export const SessionManagementHelper = (() => {
54
54
  interval: number,
55
55
  sessionRefreshInterval: number,
56
56
  redirectURL: string,
57
- authorizationEndpoint: string,
58
- isPKCEEnabled: boolean
57
+ getAuthorizationURL: (params?: GetAuthURLConfig) => Promise<string>
59
58
  ): void => {
60
59
  _clientID = clientID;
61
60
  _checkSessionEndpoint = checkSessionEndpoint;
62
61
  _sessionState = getSessionState;
63
62
  _interval = interval;
64
63
  _redirectURL = redirectURL;
65
- _authorizationEndpoint = authorizationEndpoint;
66
64
  _sessionRefreshInterval = sessionRefreshInterval;
67
- _isPKCEEnabled = isPKCEEnabled;
65
+ _getAuthorizationURL = getAuthorizationURL;
68
66
 
69
67
  if (_interval > -1) {
70
68
  initiateCheckSession();
@@ -115,17 +113,6 @@ export const SessionManagementHelper = (() => {
115
113
  clearInterval(_sessionRefreshIntervalTimeout);
116
114
  }
117
115
 
118
- const getRandomPKCEChallenge = (): string => {
119
- const chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz-_";
120
- const stringLength = 43;
121
- let randomString = "";
122
- for (let i = 0; i < stringLength; i++) {
123
- const rnum = Math.floor(Math.random() * chars.length);
124
- randomString += chars.substring(rnum, rnum + 1);
125
- }
126
- return randomString;
127
- };
128
-
129
116
  const listenToResponseFromOPIFrame = (): void => {
130
117
  async function receiveMessage(e: MessageEvent) {
131
118
  const targetOrigin = _checkSessionEndpoint;
@@ -149,7 +136,7 @@ export const SessionManagementHelper = (() => {
149
136
  window?.addEventListener("message", receiveMessage, false);
150
137
  };
151
138
 
152
- const sendPromptNoneRequest = () => {
139
+ const sendPromptNoneRequest = async () => {
153
140
  const rpIFrame = document.getElementById(RP_IFRAME) as HTMLIFrameElement;
154
141
 
155
142
  const promptNoneIFrame: HTMLIFrameElement = rpIFrame?.contentDocument?.getElementById(
@@ -170,20 +157,12 @@ export const SessionManagementHelper = (() => {
170
157
  window?.addEventListener("message", receiveMessageListener);
171
158
  }
172
159
 
173
- const promptNoneURL = new URL(_authorizationEndpoint);
174
- promptNoneURL.searchParams.set("response_type", "code");
175
- promptNoneURL.searchParams.set("client_id", _clientID);
176
- promptNoneURL.searchParams.set("scope", "openid");
177
- promptNoneURL.searchParams.set("redirect_uri", _redirectURL);
178
- promptNoneURL.searchParams.set("state", STATE);
179
- promptNoneURL.searchParams.set("prompt", "none");
180
-
181
- if(_isPKCEEnabled){
182
- promptNoneURL.searchParams.set("code_challenge_method", "S256");
183
- promptNoneURL.searchParams.set("code_challenge", getRandomPKCEChallenge());
184
- }
160
+ const promptNoneURL: string = await _getAuthorizationURL({
161
+ prompt: "none",
162
+ state: STATE
163
+ });
185
164
 
186
- promptNoneIFrame.src = promptNoneURL.toString();
165
+ promptNoneIFrame.src = promptNoneURL;
187
166
  }
188
167
  };
189
168
 
@@ -196,20 +175,21 @@ export const SessionManagementHelper = (() => {
196
175
  const receivePromptNoneResponse = async (
197
176
  setSessionState?: (sessionState: string | null) => Promise<void>
198
177
  ): Promise<boolean> => {
199
- const state = new URL(window.location.href).searchParams.get("state");
178
+ const state = new URL(window.location.href).searchParams.get(STATE_QUERY);
200
179
  const sessionState = new URL(window.location.href).searchParams.get(SESSION_STATE);
201
180
  const parent = window.parent.parent;
202
181
 
203
- if (state !== null && (state === STATE || state === SILENT_SIGN_IN_STATE)) {
182
+ if (state !== null && (state.includes(STATE) || state.includes(SILENT_SIGN_IN_STATE))) {
204
183
  // Prompt none response.
205
184
  const code = new URL(window.location.href).searchParams.get("code");
206
185
 
207
186
  if (code !== null && code.length !== 0) {
208
- if (state === SILENT_SIGN_IN_STATE) {
187
+ if (state.includes(SILENT_SIGN_IN_STATE)) {
209
188
  const message: Message<AuthorizationInfo> = {
210
189
  data: {
211
190
  code,
212
- sessionState: sessionState ?? ""
191
+ sessionState: sessionState ?? "",
192
+ state
213
193
  },
214
194
  type: CHECK_SESSION_SIGNED_IN
215
195
  };
@@ -228,7 +208,7 @@ export const SessionManagementHelper = (() => {
228
208
  const newSessionState = new URL(window.location.href).searchParams.get("session_state");
229
209
 
230
210
  if (_storage === Storage.LocalStorage || _storage === Storage.SessionStorage) {
231
- setSessionState && await setSessionState(newSessionState);
211
+ setSessionState && (await setSessionState(newSessionState));
232
212
  } else {
233
213
  const message: Message<string> = {
234
214
  data: newSessionState ?? "",
@@ -246,7 +226,7 @@ export const SessionManagementHelper = (() => {
246
226
 
247
227
  return true;
248
228
  } else {
249
- if (state === SILENT_SIGN_IN_STATE) {
229
+ if (state.includes(SILENT_SIGN_IN_STATE)) {
250
230
  const message: Message<null> = {
251
231
  type: CHECK_SESSION_SIGNED_OUT
252
232
  };
@@ -275,15 +255,16 @@ export const SessionManagementHelper = (() => {
275
255
  return false;
276
256
  };
277
257
 
278
- return (
258
+ return async (
279
259
  signOut: () => Promise<string>,
280
260
  storage: Storage,
281
261
  setSessionState: (sessionState: string) => void
282
- ): SessionManagementHelperInterface => {
262
+ ): Promise<SessionManagementHelperInterface> => {
283
263
  let rpIFrame = document.createElement("iframe");
284
264
  rpIFrame.setAttribute("id", RP_IFRAME);
285
265
  rpIFrame.style.display = "none";
286
266
 
267
+ let rpIframeLoaded: boolean = false;
287
268
  rpIFrame.onload = () => {
288
269
  rpIFrame = document.getElementById(RP_IFRAME) as HTMLIFrameElement;
289
270
 
@@ -303,6 +284,8 @@ export const SessionManagementHelper = (() => {
303
284
 
304
285
  opIFrame && rpIFrame?.contentDocument?.body?.appendChild(opIFrame);
305
286
  promptNoneIFrame && rpIFrame?.contentDocument?.body?.appendChild(promptNoneIFrame);
287
+
288
+ rpIframeLoaded = true;
306
289
  }
307
290
 
308
291
  document?.body?.appendChild(rpIFrame);
@@ -312,6 +295,14 @@ export const SessionManagementHelper = (() => {
312
295
  _storage = storage;
313
296
  _setSessionState = setSessionState;
314
297
 
298
+ const sleep = (): Promise<any> => {
299
+ return new Promise((resolve) => setTimeout(resolve, 1));
300
+ };
301
+
302
+ while (rpIframeLoaded === false) {
303
+ await sleep();
304
+ }
305
+
315
306
  return {
316
307
  initialize,
317
308
  receivePromptNoneResponse,
@@ -17,7 +17,6 @@
17
17
  */
18
18
 
19
19
  import "core-js/stable";
20
- import "regenerator-runtime/runtime";
21
20
 
22
21
  // Export the public API.
23
22
  export * from "./public-api";
@@ -21,6 +21,7 @@ import {
21
21
  BasicUserInfo,
22
22
  CustomGrantConfig,
23
23
  DecodedIDTokenPayload,
24
+ FetchResponse,
24
25
  OIDCEndpoints,
25
26
  OIDCProviderMetaData
26
27
  } from "@asgardeo/auth-js";
@@ -51,7 +52,7 @@ export interface MainThreadClientInterface {
51
52
  signInRedirectURL?: string
52
53
  ): Promise<BasicUserInfo>;
53
54
  signOut(signOutRedirectURL?: string): Promise<boolean>;
54
- requestCustomGrant(config: CustomGrantConfig): Promise<BasicUserInfo | HttpResponse>;
55
+ requestCustomGrant(config: CustomGrantConfig): Promise<BasicUserInfo | FetchResponse>;
55
56
  refreshAccessToken(): Promise<BasicUserInfo>;
56
57
  revokeAccessToken(): Promise<boolean>;
57
58
  getBasicUserInfo(): Promise<BasicUserInfo>;
@@ -65,7 +66,7 @@ export interface MainThreadClientInterface {
65
66
  }
66
67
 
67
68
  export interface WebWorkerClientInterface {
68
- requestCustomGrant(requestParams: CustomGrantConfig): Promise<HttpResponse | BasicUserInfo>;
69
+ requestCustomGrant(requestParams: CustomGrantConfig): Promise<FetchResponse | BasicUserInfo>;
69
70
  httpRequest<T = any>(config: HttpRequestConfig): Promise<HttpResponse<T>>;
70
71
  httpRequestAll<T = any>(configs: HttpRequestConfig[]): Promise<HttpResponse<T>[]>;
71
72
  enableHttpHandler(): Promise<boolean>;
@@ -33,8 +33,9 @@ export interface HttpRequestConfig extends AxiosRequestConfig {
33
33
 
34
34
  export {
35
35
  AxiosResponse as HttpResponse,
36
- Method,
37
- AxiosTransformer as HttpTransformer,
36
+ Method as HttpMethod,
37
+ AxiosRequestTransformer as HttpRequestTransformer,
38
+ AxiosResponseTransformer as HttpResponseTransformer,
38
39
  AxiosAdapter as HttpAdapter,
39
40
  AxiosBasicCredentials as HttpBasicCredentials,
40
41
  ResponseType,
@@ -23,3 +23,4 @@ export * from "./web-worker";
23
23
  export * from "./session-management-helper";
24
24
  export * from "./client-config";
25
25
  export * from "./sign-in";
26
+ export * from "./sign-out-error";
@@ -66,6 +66,7 @@ export interface AuthorizationInfo {
66
66
  code: string;
67
67
  sessionState: string;
68
68
  pkce?: string;
69
+ state: string;
69
70
  }
70
71
 
71
72
  export type MessageType =
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.com) All Rights Reserved.
3
+ *
4
+ * WSO2 Inc. licenses this file to you under the Apache License,
5
+ * Version 2.0 (the "License"); you may not use this file except
6
+ * in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing,
12
+ * software distributed under the License is distributed on an
13
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ * KIND, either express or implied. See the License for the
15
+ * specific language governing permissions and limitations
16
+ * under the License.
17
+ */
18
+
19
+ import { CustomGrantConfig } from "@asgardeo/auth-js";
20
+
21
+ /**
22
+ * SPA Custom Request Grant config model
23
+ */
24
+ export interface SPACustomGrantConfig extends CustomGrantConfig {
25
+ preventSignOutURLUpdate?: boolean;
26
+ }
@@ -16,6 +16,8 @@
16
16
  * under the License.
17
17
  */
18
18
 
19
+ import { GetAuthURLConfig } from "..";
20
+
19
21
  export interface SessionManagementHelperInterface {
20
22
  initialize(
21
23
  clientID: string,
@@ -24,8 +26,7 @@ export interface SessionManagementHelperInterface {
24
26
  interval: number,
25
27
  sessionRefreshInterval: number,
26
28
  redirectURL: string,
27
- authorizationEndpoint: string,
28
- isPKCEEnabled?: boolean
29
+ getAuthorizationURL: (params?: GetAuthURLConfig) => Promise<string>
29
30
  ): void;
30
31
  receivePromptNoneResponse(
31
32
  setSessionState?: (sessionState: string | null) => Promise<void>
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.com) All Rights Reserved.
3
+ *
4
+ * WSO2 Inc. licenses this file to you under the Apache License,
5
+ * Version 2.0 (the "License"); you may not use this file except
6
+ * in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing,
12
+ * software distributed under the License is distributed on an
13
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ * KIND, either express or implied. See the License for the
15
+ * specific language governing permissions and limitations
16
+ * under the License.
17
+ */
18
+
19
+ export interface SignOutError {
20
+ error: string;
21
+ description: string;
22
+ }
@@ -22,6 +22,7 @@ import {
22
22
  BasicUserInfo,
23
23
  CustomGrantConfig,
24
24
  DecodedIDTokenPayload,
25
+ FetchResponse,
25
26
  OIDCEndpoints
26
27
  } from "@asgardeo/auth-js";
27
28
  import { HttpRequestConfig, HttpResponse, Message } from ".";
@@ -44,10 +45,15 @@ export interface WebWorkerCoreInterface {
44
45
  enableHttpHandler(): void;
45
46
  disableHttpHandler(): void;
46
47
  getAuthorizationURL(params?: AuthorizationURLParams, signInRedirectURL?: string): Promise<AuthorizationResponse>;
47
- requestAccessToken(authorizationCode?: string, sessionState?: string, pkce?: string): Promise<BasicUserInfo>;
48
+ requestAccessToken(
49
+ authorizationCode?: string,
50
+ sessionState?: string,
51
+ pkce?: string,
52
+ state?: string
53
+ ): Promise<BasicUserInfo>;
48
54
  signOut(signOutRedirectURL?: string): Promise<string>;
49
55
  getSignOutURL(signOutRedirectURL?: string): Promise<string>;
50
- requestCustomGrant(config: CustomGrantConfig): Promise<BasicUserInfo | HttpResponse>;
56
+ requestCustomGrant(config: CustomGrantConfig): Promise<BasicUserInfo | FetchResponse>;
51
57
  refreshAccessToken(): Promise<BasicUserInfo>;
52
58
  revokeAccessToken(): Promise<boolean>;
53
59
  getBasicUserInfo(): Promise<BasicUserInfo>;
package/src/public-api.ts CHANGED
@@ -26,6 +26,7 @@ export * from "./models";
26
26
  export * from "./utils/spa-utils"
27
27
 
28
28
  // Constants
29
- export * from "@asgardeo/auth-js";
30
29
  export * from "./constants/storage";
31
30
  export * from "./constants/hooks";
31
+
32
+ export * from "@asgardeo/auth-js";
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Copyright (c) 2019, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
3
+ *
4
+ * WSO2 Inc. licenses this file to you under the Apache License,
5
+ * Version 2.0 (the "License"); you may not use this file except
6
+ * in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing,
12
+ * software distributed under the License is distributed on an
13
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ * KIND, either express or implied. See the License for the
15
+ * specific language governing permissions and limitations
16
+ * under the License.
17
+ */
18
+
19
+ import { Buffer } from "buffer";
20
+ import { CryptoUtils, JWKInterface } from "@asgardeo/auth-js";
21
+ import base64url from "base64url";
22
+ import sha256 from "fast-sha256";
23
+ import { createLocalJWKSet, jwtVerify } from "jose";
24
+ import { FlattenedJWSInput, GetKeyFunction, JWSHeaderParameters } from "jose/dist/types/types";
25
+ import randombytes from "randombytes";
26
+
27
+ export class SPACryptoUtils
28
+ implements CryptoUtils<Buffer | string, GetKeyFunction<JWSHeaderParameters, FlattenedJWSInput>>
29
+ {
30
+ /**
31
+ * Get URL encoded string.
32
+ *
33
+ * @returns {string} base 64 url encoded value.
34
+ */
35
+ public base64URLEncode(value: Buffer | string): string {
36
+ return base64url.encode(value).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
37
+ }
38
+
39
+ public base64URLDecode(value: string): string {
40
+ return base64url.decode(value).toString();
41
+ }
42
+
43
+ public hashSha256(data: string): string | Buffer {
44
+ return Buffer.from(sha256(new TextEncoder().encode(data)));
45
+ }
46
+
47
+ public generateRandomBytes(length: number): string | Buffer {
48
+ return randombytes(length);
49
+ }
50
+
51
+ public parseJwk(key: Partial<JWKInterface>): Promise<GetKeyFunction<JWSHeaderParameters, FlattenedJWSInput>> {
52
+ return Promise.resolve(
53
+ createLocalJWKSet({
54
+ keys: [ key ]
55
+ })
56
+ );
57
+ }
58
+
59
+ public verifyJwt(
60
+ idToken: string,
61
+ jwk: GetKeyFunction<JWSHeaderParameters, FlattenedJWSInput>,
62
+ algorithms: string[],
63
+ clientID: string,
64
+ issuer: string,
65
+ subject: string,
66
+ clockTolerance?: number
67
+ ): Promise<boolean> {
68
+ return jwtVerify(idToken, jwk, {
69
+ algorithms: algorithms,
70
+ audience: clientID,
71
+ clockTolerance: clockTolerance,
72
+ issuer: issuer,
73
+ subject: subject
74
+ }).then(() => {
75
+ return Promise.resolve(true);
76
+ });
77
+ }
78
+ }
@@ -16,12 +16,15 @@
16
16
  * under the License.
17
17
  */
18
18
 
19
- import { AsgardeoAuthClient, PKCE_CODE_VERIFIER, SIGN_OUT_URL } from "@asgardeo/auth-js";
19
+ import { AsgardeoAuthClient, SIGN_OUT_SUCCESS_PARAM, SIGN_OUT_URL } from "@asgardeo/auth-js";
20
+ import { SignOutError } from "..";
20
21
  import {
21
22
  ERROR,
23
+ ERROR_DESCRIPTION,
22
24
  INITIALIZED_SILENT_SIGN_IN,
23
25
  PROMPT_NONE_REQUEST_SENT,
24
- SILENT_SIGN_IN_STATE
26
+ SILENT_SIGN_IN_STATE,
27
+ STATE_QUERY
25
28
  } from "../constants";
26
29
 
27
30
  export class SPAUtils {
@@ -34,12 +37,12 @@ export class SPAUtils {
34
37
  history.pushState({}, document.title, url.replace(/\?code=.*$/, ""));
35
38
  }
36
39
 
37
- public static getPKCE(): string {
38
- return sessionStorage.getItem(PKCE_CODE_VERIFIER) ?? "";
40
+ public static getPKCE(pkceKey: string): string {
41
+ return sessionStorage.getItem(pkceKey) ?? "";
39
42
  }
40
43
 
41
- public static setPKCE(pkce: string): void {
42
- sessionStorage.setItem(PKCE_CODE_VERIFIER, pkce);
44
+ public static setPKCE(pkceKey: string, pkce: string): void {
45
+ sessionStorage.setItem(pkceKey, pkce);
43
46
  }
44
47
 
45
48
  public static setSignOutURL(url: string): void {
@@ -50,8 +53,8 @@ export class SPAUtils {
50
53
  return sessionStorage.getItem(SIGN_OUT_URL) ?? "";
51
54
  }
52
55
 
53
- public static removePKCE(): void {
54
- sessionStorage.removeItem(PKCE_CODE_VERIFIER);
56
+ public static removePKCE(pkceKey: string): void {
57
+ sessionStorage.removeItem(pkceKey);
55
58
  }
56
59
 
57
60
  /**
@@ -121,6 +124,23 @@ export class SPAUtils {
121
124
  return false;
122
125
  }
123
126
 
127
+ public static didSignOutFail(): boolean | SignOutError {
128
+ if (AsgardeoAuthClient.didSignOutFail(window.location.href)) {
129
+ const url: URL = new URL(window.location.href);
130
+ const error: string | null = url.searchParams.get(ERROR);
131
+ const description: string | null = url.searchParams.get(ERROR_DESCRIPTION);
132
+ const newUrl = window.location.href.split("?")[0];
133
+ history.pushState({}, document.title, newUrl);
134
+
135
+ return {
136
+ description: description ?? "",
137
+ error: error ?? ""
138
+ };
139
+ }
140
+
141
+ return false;
142
+ }
143
+
124
144
  /**
125
145
  * Checks if the URL the user agent is redirected to after an authorization request has the state parameter.
126
146
  *
@@ -129,7 +149,7 @@ export class SPAUtils {
129
149
  public static isSilentStatePresentInURL(): boolean {
130
150
  const state = new URL(window.location.href).searchParams.get("state");
131
151
 
132
- return state === SILENT_SIGN_IN_STATE;
152
+ return state?.includes(SILENT_SIGN_IN_STATE) ?? false;
133
153
  }
134
154
 
135
155
  /**
@@ -153,7 +173,10 @@ export class SPAUtils {
153
173
  * @returns {boolean} - True if the URL contains an error.
154
174
  */
155
175
  public static hasErrorInURL(url: string = window.location.href): boolean {
156
- return !!new URL(url).searchParams.get(ERROR);
176
+ const urlObject: URL = new URL(url);
177
+ return (
178
+ !!urlObject.searchParams.get(ERROR) && urlObject.searchParams.get(STATE_QUERY) !== SIGN_OUT_SUCCESS_PARAM
179
+ );
157
180
  }
158
181
 
159
182
  /**
@@ -103,7 +103,7 @@ ctx.onmessage = async ({ data, ports }) => {
103
103
  break;
104
104
  case REQUEST_ACCESS_TOKEN:
105
105
  webWorker
106
- .requestAccessToken(data?.data?.code, data?.data?.sessionState, data?.data?.pkce)
106
+ .requestAccessToken(data?.data?.code, data?.data?.sessionState, data?.data?.pkce, data?.data?.state)
107
107
  .then((response: BasicUserInfo) => {
108
108
  port.postMessage(MessageUtils.generateSuccessMessage(response));
109
109
  })
@@ -23,8 +23,10 @@ import {
23
23
  BasicUserInfo,
24
24
  CustomGrantConfig,
25
25
  DecodedIDTokenPayload,
26
+ FetchResponse,
26
27
  OIDCEndpoints,
27
28
  SESSION_STATE,
29
+ STATE,
28
30
  Store,
29
31
  TokenResponse
30
32
  } from "@asgardeo/auth-js";
@@ -41,21 +43,19 @@ import {
41
43
  WebWorkerCoreInterface
42
44
  } from "../models";
43
45
  import { MemoryStore } from "../stores";
46
+ import { SPACryptoUtils } from "../utils/crypto-utils";
44
47
 
45
48
  export const WebWorkerCore = async (
46
49
  config: AuthClientConfig<WebWorkerClientConfig>
47
50
  ): Promise<WebWorkerCoreInterface> => {
48
51
  const _store: Store = new MemoryStore();
49
- const _authenticationClient = new AsgardeoAuthClient<WebWorkerClientConfig>(_store);
52
+ const _cryptoUtils: SPACryptoUtils = new SPACryptoUtils();
53
+ const _authenticationClient = new AsgardeoAuthClient<WebWorkerClientConfig>(_store, _cryptoUtils);
50
54
  await _authenticationClient.initialize(config);
51
55
 
52
56
  const _spaHelper = new SPAHelper<WebWorkerClientConfig>(_authenticationClient);
53
57
  const _dataLayer = _authenticationClient.getDataLayer();
54
58
 
55
- let _onHttpRequestStart: () => void;
56
- let _onHttpRequestSuccess: (response: HttpResponse) => void;
57
- let _onHttpRequestFinish: () => void;
58
- let _onHttpRequestError: (error: HttpError) => void;
59
59
  const _httpClient: HttpClientInstance = HttpClient.getInstance();
60
60
 
61
61
  const attachToken = async (request: HttpRequestConfig): Promise<void> => {
@@ -247,7 +247,11 @@ export const WebWorkerCore = async (
247
247
  return _authenticationClient
248
248
  .getAuthorizationURL(params)
249
249
  .then(async (url: string) => {
250
- return { authorizationURL: url, pkce: (await _authenticationClient.getPKCECode()) as string };
250
+ const urlObject: URL = new URL(url);
251
+ const state: string = urlObject.searchParams.get(STATE) ?? "";
252
+ const pkce: string = await _authenticationClient.getPKCECode(state);
253
+
254
+ return { authorizationURL: url, pkce: pkce };
251
255
  })
252
256
  .catch((error) => Promise.reject(error));
253
257
  };
@@ -262,17 +266,18 @@ export const WebWorkerCore = async (
262
266
  const requestAccessToken = async (
263
267
  authorizationCode?: string,
264
268
  sessionState?: string,
265
- pkce?: string
269
+ pkce?: string,
270
+ state?: string
266
271
  ): Promise<BasicUserInfo> => {
267
272
  const config = await _dataLayer.getConfigData();
268
273
 
269
274
  if (pkce && config.enablePKCE) {
270
- await _authenticationClient.setPKCECode(pkce);
275
+ await _authenticationClient.setPKCECode(pkce, state ?? "");
271
276
  }
272
277
 
273
278
  if (authorizationCode) {
274
279
  return _authenticationClient
275
- .requestAccessToken(authorizationCode, sessionState ?? "")
280
+ .requestAccessToken(authorizationCode, sessionState ?? "", state ?? "'")
276
281
  .then(() => {
277
282
  _spaHelper.refreshAccessTokenAutomatically();
278
283
 
@@ -304,7 +309,7 @@ export const WebWorkerCore = async (
304
309
  return await _authenticationClient.getSignOutURL();
305
310
  };
306
311
 
307
- const requestCustomGrant = async (config: CustomGrantConfig): Promise<BasicUserInfo | HttpResponse> => {
312
+ const requestCustomGrant = async (config: CustomGrantConfig): Promise<BasicUserInfo | FetchResponse> => {
308
313
  let useDefaultEndpoint = true;
309
314
  let matches = false;
310
315
  const clientConfig = await _dataLayer.getConfigData();
@@ -329,13 +334,13 @@ export const WebWorkerCore = async (
329
334
  if (useDefaultEndpoint || matches) {
330
335
  return _authenticationClient
331
336
  .requestCustomGrant(config)
332
- .then(async (response: HttpResponse | TokenResponse) => {
337
+ .then(async (response: FetchResponse | TokenResponse) => {
333
338
  if (config.returnsSession) {
334
339
  _spaHelper.refreshAccessTokenAutomatically();
335
340
 
336
341
  return _authenticationClient.getBasicUserInfo();
337
342
  } else {
338
- return response as HttpResponse;
343
+ return response as FetchResponse;
339
344
  }
340
345
  })
341
346
  .catch((error) => {
@@ -356,22 +361,19 @@ export const WebWorkerCore = async (
356
361
  }
357
362
  };
358
363
 
359
- const refreshAccessToken = (): Promise<BasicUserInfo> => {
360
- return _authenticationClient
361
- .refreshAccessToken()
362
- .then(() => {
363
- getCustomGrantConfigData().then((customGrantConfig) => {
364
- if(customGrantConfig) {
365
- requestCustomGrant(customGrantConfig)
366
- }
367
- });
368
- _spaHelper.refreshAccessTokenAutomatically();
364
+ const refreshAccessToken = async (): Promise<BasicUserInfo> => {
365
+ try {
366
+ await _authenticationClient.refreshAccessToken();
367
+ const customGrantConfig = await getCustomGrantConfigData();
368
+ if (customGrantConfig) {
369
+ await requestCustomGrant(customGrantConfig);
370
+ }
371
+ _spaHelper.refreshAccessTokenAutomatically();
369
372
 
370
- return _authenticationClient.getBasicUserInfo();
371
- })
372
- .catch((error) => {
373
- return Promise.reject(error);
374
- });
373
+ return _authenticationClient.getBasicUserInfo();
374
+ } catch (error) {
375
+ return Promise.reject(error);
376
+ }
375
377
  };
376
378
 
377
379
  const revokeAccessToken = (): Promise<boolean> => {