From d543202d79dbba23f7efd07a9dfac8a68f914d57 Mon Sep 17 00:00:00 2001 From: kavindadewmith Date: Fri, 23 Jan 2026 14:36:31 +0530 Subject: [PATCH 1/6] Refactor HttpClient to support instance-specific instances for multi-auth context --- .../__legacy__/clients/main-thread-client.ts | 2 +- .../http-client/clients/axios-http-client.ts | 75 ++++++++++--------- .../http-client/models/http-client.ts | 2 +- 3 files changed, 41 insertions(+), 38 deletions(-) diff --git a/packages/browser/src/__legacy__/clients/main-thread-client.ts b/packages/browser/src/__legacy__/clients/main-thread-client.ts index cd0d9908d..79900c12b 100755 --- a/packages/browser/src/__legacy__/clients/main-thread-client.ts +++ b/packages/browser/src/__legacy__/clients/main-thread-client.ts @@ -85,7 +85,7 @@ export const MainThreadClient = async ( let _getSignOutURLFromSessionStorage: boolean = false; - const _httpClient: HttpClientInstance = HttpClient.getInstance(); + const _httpClient: HttpClientInstance = HttpClient.getInstance(instanceID); let _isHttpHandlerEnabled: boolean = true; let _httpErrorCallback: (error: HttpError) => void | Promise; let _httpFinishCallback: () => void; diff --git a/packages/browser/src/__legacy__/http-client/clients/axios-http-client.ts b/packages/browser/src/__legacy__/http-client/clients/axios-http-client.ts index d6f56f86e..7c675ec6b 100755 --- a/packages/browser/src/__legacy__/http-client/clients/axios-http-client.ts +++ b/packages/browser/src/__legacy__/http-client/clients/axios-http-client.ts @@ -41,9 +41,9 @@ import {HttpClientInstance, HttpClientInterface, HttpClientStatic} from '../mode */ @staticDecorator>() export class HttpClient implements HttpClientInterface { - private static axiosInstance: HttpClientInstance; - private static clientInstance: HttpClient; - private static isHandlerEnabled: boolean; + private static instances: Map = new Map(); + private static clientInstances: Map = new Map(); + private isHandlerEnabled: boolean = true; private attachToken: (request: HttpRequestConfig) => Promise = () => Promise.resolve(); private requestStartCallback: (request: HttpRequestConfig) => void = () => null; private requestSuccessCallback: (response: HttpResponse) => void = () => null; @@ -66,48 +66,51 @@ export class HttpClient implements HttpClientInterface await this.clientInstance.requestHandler(request as HttpRequestConfig) as InternalAxiosRequestConfig); + axiosInstance.interceptors.request.use(async (request: InternalAxiosRequestConfig) => await clientInstance.requestHandler(request as HttpRequestConfig) as InternalAxiosRequestConfig); // Register response interceptor - this.axiosInstance.interceptors.response.use( - response => this.clientInstance.successHandler(response), - error => this.clientInstance.errorHandler(error), + axiosInstance.interceptors.response.use( + response => clientInstance.successHandler(response), + error => clientInstance.errorHandler(error), ); // Add the missing helper methods from axios - this.axiosInstance.all = axios.all; - this.axiosInstance.spread = axios.spread; + axiosInstance.all = axios.all; + axiosInstance.spread = axios.spread; // Add the init method from the `HttpClient` instance. - this.axiosInstance.init = this.clientInstance.init; + axiosInstance.init = clientInstance.init; // Add the handler enabling & disabling methods to the instance. - this.axiosInstance.enableHandler = this.clientInstance.enableHandler; - this.axiosInstance.disableHandler = this.clientInstance.disableHandler; - this.axiosInstance.disableHandlerWithTimeout = this.clientInstance.disableHandlerWithTimeout; - this.axiosInstance.setHttpRequestStartCallback = this.clientInstance.setHttpRequestStartCallback; - this.axiosInstance.setHttpRequestSuccessCallback = this.clientInstance.setHttpRequestSuccessCallback; - this.axiosInstance.setHttpRequestErrorCallback = this.clientInstance.setHttpRequestErrorCallback; - this.axiosInstance.setHttpRequestFinishCallback = this.clientInstance.setHttpRequestFinishCallback; - return this.axiosInstance; + axiosInstance.enableHandler = clientInstance.enableHandler; + axiosInstance.disableHandler = clientInstance.disableHandler; + axiosInstance.disableHandlerWithTimeout = clientInstance.disableHandlerWithTimeout; + axiosInstance.setHttpRequestStartCallback = clientInstance.setHttpRequestStartCallback; + axiosInstance.setHttpRequestSuccessCallback = clientInstance.setHttpRequestSuccessCallback; + axiosInstance.setHttpRequestErrorCallback = clientInstance.setHttpRequestErrorCallback; + axiosInstance.setHttpRequestFinishCallback = clientInstance.setHttpRequestFinishCallback; + + this.instances.set(instanceId, axiosInstance); + return axiosInstance; } /** @@ -134,7 +137,7 @@ export class HttpClient implements HttpClientInterface Promise, ): Promise { - HttpClient.isHandlerEnabled = isHandlerEnabled; + this.isHandlerEnabled = isHandlerEnabled; this.attachToken = attachToken; } @@ -203,14 +206,14 @@ export class HttpClient implements HttpClientInterface { - HttpClient.isHandlerEnabled = true; + this.isHandlerEnabled = true; }, timeout); } diff --git a/packages/browser/src/__legacy__/http-client/models/http-client.ts b/packages/browser/src/__legacy__/http-client/models/http-client.ts index d6e612fed..9c0783c6b 100644 --- a/packages/browser/src/__legacy__/http-client/models/http-client.ts +++ b/packages/browser/src/__legacy__/http-client/models/http-client.ts @@ -23,7 +23,7 @@ import { HttpError, HttpResponse } from "../../models"; * Http client interface with static functions. */ export interface HttpClientStatic { - getInstance(): S; + getInstance(instanceId?: number): S; } /** From 7923936ec81e6c74efe3b306ab33120a9c9645fb Mon Sep 17 00:00:00 2001 From: kavindadewmith Date: Wed, 18 Feb 2026 18:34:10 +0530 Subject: [PATCH 2/6] Refactor HttpClient to initialize isHandlerEnabled value as undefined --- .../src/__legacy__/http-client/clients/axios-http-client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/browser/src/__legacy__/http-client/clients/axios-http-client.ts b/packages/browser/src/__legacy__/http-client/clients/axios-http-client.ts index 7c675ec6b..fc84a3fb8 100755 --- a/packages/browser/src/__legacy__/http-client/clients/axios-http-client.ts +++ b/packages/browser/src/__legacy__/http-client/clients/axios-http-client.ts @@ -43,7 +43,7 @@ import {HttpClientInstance, HttpClientInterface, HttpClientStatic} from '../mode export class HttpClient implements HttpClientInterface { private static instances: Map = new Map(); private static clientInstances: Map = new Map(); - private isHandlerEnabled: boolean = true; + private isHandlerEnabled: boolean; private attachToken: (request: HttpRequestConfig) => Promise = () => Promise.resolve(); private requestStartCallback: (request: HttpRequestConfig) => void = () => null; private requestSuccessCallback: (response: HttpResponse) => void = () => null; From cc208b68c50a286517628ab40817ab5ff33f9c0c Mon Sep 17 00:00:00 2001 From: kavindadewmith Date: Thu, 19 Feb 2026 15:41:47 +0530 Subject: [PATCH 3/6] Initialize isHandlerEnabled to true and bind handler methods to correct 'this' context in HttpClient --- .../src/__legacy__/http-client/clients/axios-http-client.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/browser/src/__legacy__/http-client/clients/axios-http-client.ts b/packages/browser/src/__legacy__/http-client/clients/axios-http-client.ts index fc84a3fb8..9ce419166 100755 --- a/packages/browser/src/__legacy__/http-client/clients/axios-http-client.ts +++ b/packages/browser/src/__legacy__/http-client/clients/axios-http-client.ts @@ -43,7 +43,7 @@ import {HttpClientInstance, HttpClientInterface, HttpClientStatic} from '../mode export class HttpClient implements HttpClientInterface { private static instances: Map = new Map(); private static clientInstances: Map = new Map(); - private isHandlerEnabled: boolean; + private isHandlerEnabled: boolean = true; private attachToken: (request: HttpRequestConfig) => Promise = () => Promise.resolve(); private requestStartCallback: (request: HttpRequestConfig) => void = () => null; private requestSuccessCallback: (response: HttpResponse) => void = () => null; @@ -59,6 +59,9 @@ export class HttpClient implements HttpClientInterface Date: Thu, 19 Feb 2026 15:58:41 +0530 Subject: [PATCH 4/6] =?UTF-8?q?Add=20changeset=20=F0=9F=A6=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changeset/light-queens-rest.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/light-queens-rest.md diff --git a/.changeset/light-queens-rest.md b/.changeset/light-queens-rest.md new file mode 100644 index 000000000..6cdfdf1e4 --- /dev/null +++ b/.changeset/light-queens-rest.md @@ -0,0 +1,5 @@ +--- +'@asgardeo/browser': minor +--- + +Fix failure of calling authenticated APIs from secondary AsgardeoProvider Instances in Multi Provider scenarios From 74bdd93e84ba4589883106531d1cb53c7e622064 Mon Sep 17 00:00:00 2001 From: kavindadewmith Date: Mon, 23 Feb 2026 09:04:12 +0530 Subject: [PATCH 5/6] Add instance destruction method to HttpClient and update WebWorkerCore to support instance ID - Implement `destroyInstance` method in `HttpClient` to clean up instances and prevent memory leaks. - Update `WebWorkerCore` to accept an `instanceID` parameter for better instance management. - Modified `workerReceiver` to handle instance ID during initialization and pass it to `WebWorkerCore`. --- .../__legacy__/clients/web-worker-client.ts | 1782 ++++++++--------- .../http-client/clients/axios-http-client.ts | 17 + .../src/__legacy__/worker/worker-core.ts | 487 ++--- .../src/__legacy__/worker/worker-receiver.ts | 629 +++--- 4 files changed, 1467 insertions(+), 1448 deletions(-) diff --git a/packages/browser/src/__legacy__/clients/web-worker-client.ts b/packages/browser/src/__legacy__/clients/web-worker-client.ts index 615295409..99856e4be 100755 --- a/packages/browser/src/__legacy__/clients/web-worker-client.ts +++ b/packages/browser/src/__legacy__/clients/web-worker-client.ts @@ -1,891 +1,891 @@ -/** - * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { - AsgardeoAuthClient, - AsgardeoAuthException, - AuthClientConfig, - User, - IsomorphicCrypto, - TokenExchangeRequestConfig, - IdToken, - ExtendedAuthorizeRequestUrlParams, - OIDCEndpoints, - OIDCRequestConstants, - Storage, - extractPkceStorageKeyFromState, -} from '@asgardeo/javascript'; -import { - DISABLE_HTTP_HANDLER, - ENABLE_HTTP_HANDLER, - GET_AUTH_URL, - GET_BASIC_USER_INFO, - GET_CONFIG_DATA, - GET_CRYPTO_HELPER, - GET_DECODED_IDP_ID_TOKEN, - GET_DECODED_ID_TOKEN, - GET_ID_TOKEN, - GET_OIDC_SERVICE_ENDPOINTS, - GET_SIGN_OUT_URL, - HTTP_REQUEST, - HTTP_REQUEST_ALL, - INIT, - IS_AUTHENTICATED, - REFRESH_ACCESS_TOKEN, - REQUEST_ACCESS_TOKEN, - REQUEST_CUSTOM_GRANT, - REQUEST_FINISH, - REQUEST_START, - REQUEST_SUCCESS, - REVOKE_ACCESS_TOKEN, - SET_SESSION_STATE, - SIGN_OUT, - SILENT_SIGN_IN_STATE, - START_AUTO_REFRESH_TOKEN, - UPDATE_CONFIG, -} from '../constants'; -import {AuthenticationHelper, SPAHelper, SessionManagementHelper} from '../helpers'; -import { - AuthorizationInfo, - AuthorizationResponse, - HttpClient, - HttpError, - HttpRequestConfig, - HttpResponse, - Message, - ResponseMessage, - WebWorkerClientConfig, - WebWorkerClientInterface, -} from '../models'; -import {SPACustomGrantConfig} from '../models/request-custom-grant'; -import {BrowserStorage} from '../models/storage'; -import {LocalStore, MemoryStore, SessionStore} from '../stores'; -import {SPAUtils} from '../utils'; -import {SPACryptoUtils} from '../utils/crypto-utils'; - -const initiateStore = (store: BrowserStorage | undefined): Storage => { - switch (store) { - case BrowserStorage.LocalStorage: - return new LocalStore(); - case BrowserStorage.SessionStorage: - return new SessionStore(); - case BrowserStorage.BrowserMemory: - return new MemoryStore(); - default: - return new SessionStore(); - } -}; - -export const WebWorkerClient = async ( - instanceID: number, - config: AuthClientConfig, - webWorker: new () => Worker, - getAuthHelper: ( - authClient: AsgardeoAuthClient, - spaHelper: SPAHelper, - ) => AuthenticationHelper, -): Promise => { - /** - * HttpClient handlers - */ - let httpClientHandlers: HttpClient; - /** - * API request time out. - */ - const _requestTimeout: number = config?.requestTimeout ?? 60000; - let _isHttpHandlerEnabled: boolean = true; - let _getSignOutURLFromSessionStorage: boolean = false; - - const _store: Storage = initiateStore(config.storage as BrowserStorage); - const _cryptoUtils: SPACryptoUtils = new SPACryptoUtils(); - const _authenticationClient = new AsgardeoAuthClient(); - await _authenticationClient.initialize(config, _store, _cryptoUtils, instanceID); - const _spaHelper = new SPAHelper(_authenticationClient); - - const _sessionManagementHelper = await SessionManagementHelper( - async () => { - const message: Message = { - type: SIGN_OUT, - }; - - try { - const signOutURL = await communicate(message); - - return signOutURL; - } catch { - return SPAUtils.getSignOutUrl(config.clientId, instanceID); - } - }, - config.storage as BrowserStorage, - (sessionState: string) => setSessionState(sessionState), - ); - - const _authenticationHelper: AuthenticationHelper = getAuthHelper( - _authenticationClient, - _spaHelper, - ); - - const worker: Worker = new webWorker(); - - const communicate = (message: Message): Promise => { - const channel = new MessageChannel(); - - worker.postMessage(message, [channel.port2]); - - return new Promise((resolve, reject) => { - const timer = setTimeout(() => { - reject( - new AsgardeoAuthException( - 'SPA-WEB_WORKER_CLIENT-COM-TO01', - 'Operation timed out.', - 'No response was received from the web worker for ' + - _requestTimeout / 1000 + - ' since dispatching the request', - ), - ); - }, _requestTimeout); - - return (channel.port1.onmessage = ({data}: {data: ResponseMessage}) => { - clearTimeout(timer); - channel.port1.close(); - channel.port2.close(); - - if (data?.success) { - const responseData = data?.data ? JSON.parse(data?.data) : null; - if (data?.blob) { - responseData.data = data?.blob; - } - - resolve(responseData); - } else { - let error = null; - if (data.error) { - try { - error = JSON.parse(data.error); - } catch (parseError) { - // If JSON parsing fails, create a proper error object - error = new AsgardeoAuthException( - 'SPA-WEB_WORKER_CLIENT-COM-PE01', - 'Worker communication error.', - `Failed to parse worker error response: ${data.error}`, - ); - } - } else { - error = new AsgardeoAuthException( - 'SPA-WEB_WORKER_CLIENT-COM-UE01', - 'Unknown worker error.', - 'An unknown error occurred in the web worker.', - ); - } - reject(error); - } - }); - }); - }; - - /** - * Allows using custom grant types. - * - * @param {CustomGrantRequestParams} requestParams Request Parameters. - * - * @returns {Promise} A promise that resolves with a boolean value or the request - * response if the the `returnResponse` attribute in the `requestParams` object is set to `true`. - */ - const exchangeToken = (requestParams: SPACustomGrantConfig): Promise => { - const message: Message = { - data: requestParams, - type: REQUEST_CUSTOM_GRANT, - }; - - return communicate(message) - .then(response => { - if (requestParams.preventSignOutURLUpdate) { - _getSignOutURLFromSessionStorage = true; - } - - return Promise.resolve(response); - }) - .catch(error => { - return Promise.reject(error); - }); - }; - - /** - * - * Send the API request to the web worker and returns the response. - * - * @param {HttpRequestConfig} config The Http Request Config object - * - * @returns {Promise} A promise that resolves with the response data. - */ - const httpRequest = (config: HttpRequestConfig): Promise> => { - /** - * - * Currently FormData is not supported to send to a web worker - * - * Below workaround will represent FormData object as a JSON. - * This workaround will not be needed once FormData object is made cloneable - * Reference: https://github.com/whatwg/xhr/issues/55 - */ - if (config?.data && config?.data instanceof FormData) { - config.data = {...Object.fromEntries((config?.data as any).entries()), formData: true}; - } - - const message: Message = { - data: config, - type: HTTP_REQUEST, - }; - - return communicate>(message) - .then(response => { - return Promise.resolve(response); - }) - .catch(async error => { - if (_isHttpHandlerEnabled) { - if (typeof httpClientHandlers.requestErrorCallback === 'function') { - await httpClientHandlers.requestErrorCallback(error); - } - if (typeof httpClientHandlers.requestFinishCallback === 'function') { - httpClientHandlers.requestFinishCallback(); - } - } - - return Promise.reject(error); - }); - }; - - /** - * - * Send multiple API requests to the web worker and returns the response. - * Similar `axios.spread` in functionality. - * - * @param {HttpRequestConfig[]} configs - The Http Request Config object - * - * @returns {Promise[]>} A promise that resolves with the response data. - */ - const httpRequestAll = (configs: HttpRequestConfig[]): Promise[]> => { - const message: Message = { - data: configs, - type: HTTP_REQUEST_ALL, - }; - - return communicate[]>(message) - .then(response => { - return Promise.resolve(response); - }) - .catch(async error => { - if (_isHttpHandlerEnabled) { - if (typeof httpClientHandlers.requestErrorCallback === 'function') { - await httpClientHandlers.requestErrorCallback(error); - } - if (typeof httpClientHandlers.requestFinishCallback === 'function') { - httpClientHandlers.requestFinishCallback(); - } - } - - return Promise.reject(error); - }); - }; - - const enableHttpHandler = (): Promise => { - const message: Message = { - type: ENABLE_HTTP_HANDLER, - }; - return communicate(message) - .then(() => { - _isHttpHandlerEnabled = true; - - return Promise.resolve(true); - }) - .catch(error => { - return Promise.reject(error); - }); - }; - - const disableHttpHandler = (): Promise => { - const message: Message = { - type: DISABLE_HTTP_HANDLER, - }; - return communicate(message) - .then(() => { - _isHttpHandlerEnabled = false; - - return Promise.resolve(true); - }) - .catch(error => { - return Promise.reject(error); - }); - }; - - /** - * Initializes the object with authentication parameters. - * - * @param {ConfigInterface} config The configuration object. - * - * @returns {Promise} Promise that resolves when initialization is successful. - * - */ - const initialize = (): Promise => { - if (!httpClientHandlers) { - httpClientHandlers = { - requestErrorCallback: () => Promise.resolve(), - requestFinishCallback: () => null, - requestStartCallback: () => null, - requestSuccessCallback: () => null, - }; - } - - worker.onmessage = ({data}) => { - switch (data.type) { - case REQUEST_FINISH: - httpClientHandlers?.requestFinishCallback && httpClientHandlers?.requestFinishCallback(); - break; - case REQUEST_START: - httpClientHandlers?.requestStartCallback && httpClientHandlers?.requestStartCallback(); - break; - case REQUEST_SUCCESS: - httpClientHandlers?.requestSuccessCallback && - httpClientHandlers?.requestSuccessCallback(data.data ? JSON.parse(data.data) : null); - break; - } - }; - - const message: Message> = { - data: config, - type: INIT, - }; - - return communicate, null>(message) - .then(() => { - return Promise.resolve(true); - }) - .catch(error => { - return Promise.reject(error); - }); - }; - - const setSessionState = (sessionState: string | null): Promise => { - const message: Message = { - data: sessionState, - type: SET_SESSION_STATE, - }; - - return communicate(message); - }; - - const startAutoRefreshToken = (): Promise => { - const message: Message = { - type: START_AUTO_REFRESH_TOKEN, - }; - - return communicate(message); - }; - - const checkSession = async (): Promise => { - const oidcEndpoints: OIDCEndpoints = await getOpenIDProviderEndpoints(); - const config: AuthClientConfig = await getConfigData(); - - _authenticationHelper.initializeSessionManger( - config, - oidcEndpoints, - async () => (await _authenticationClient.getUserSession()).sessionState, - async (params?: ExtendedAuthorizeRequestUrlParams): Promise => - (await getSignInUrl(params)).authorizationURL, - _sessionManagementHelper, - ); - }; - - const constructSilentSignInUrl = async (additionalParams: Record = {}): Promise => { - const config: AuthClientConfig = await getConfigData(); - const message: Message = { - data: { - prompt: 'none', - state: SILENT_SIGN_IN_STATE, - ...additionalParams, - }, - type: GET_AUTH_URL, - }; - - const response: AuthorizationResponse = await communicate( - message, - ); - - const pkceKey: string = extractPkceStorageKeyFromState( - new URL(response.authorizationURL).searchParams.get(OIDCRequestConstants.Params.STATE) ?? '', - ); - - response.pkce && config.enablePKCE && SPAUtils.setPKCE(pkceKey, response.pkce); - - const urlString: string = response.authorizationURL; - - // Replace form_post with query - const urlObject = new URL(urlString); - urlObject.searchParams.set('response_mode', 'query'); - const url: string = urlObject.toString(); - - return url; - }; - - /** - * This method checks if there is an active user session in the server by sending a prompt none request. - * If the user is signed in, this method sends a token request. Returns false otherwise. - * - * @return {Promise, - tokenRequestConfig?: {params: Record}, - ): Promise => { - return await _authenticationHelper.signInSilently( - constructSilentSignInUrl, - requestAccessToken, - _sessionManagementHelper, - additionalParams, - tokenRequestConfig, - ); - }; - - /** - * Generates an authorization URL. - * - * @param {ExtendedAuthorizeRequestUrlParams} params Authorization URL params. - * @returns {Promise} Authorization URL. - */ - const getSignInUrl = async (params?: ExtendedAuthorizeRequestUrlParams): Promise => { - const config: AuthClientConfig = await getConfigData(); - - const message: Message = { - data: params, - type: GET_AUTH_URL, - }; - - return communicate(message) - .then(async (response: AuthorizationResponse) => { - if (response.pkce && config.enablePKCE) { - const pkceKey: string = extractPkceStorageKeyFromState( - new URL(response.authorizationURL).searchParams.get(OIDCRequestConstants.Params.STATE) ?? '', - ); - - SPAUtils.setPKCE(pkceKey, response.pkce); - } - - return Promise.resolve(response); - }) - .catch(error => { - return Promise.reject(error); - }); - }; - - const requestAccessToken = async ( - resolvedAuthorizationCode: string, - resolvedSessionState: string, - resolvedState: string, - tokenRequestConfig?: { - params: Record; - }, - ): Promise => { - const config: AuthClientConfig = await getConfigData(); - const pkceKey: string = extractPkceStorageKeyFromState(resolvedState); - - const message: Message = { - data: { - code: resolvedAuthorizationCode, - pkce: config.enablePKCE ? SPAUtils.getPKCE(pkceKey) : undefined, - sessionState: resolvedSessionState, - state: resolvedState, - tokenRequestConfig, - }, - type: REQUEST_ACCESS_TOKEN, - }; - - config.enablePKCE && SPAUtils.removePKCE(pkceKey); - - return communicate(message) - .then(response => { - const message: Message = { - type: GET_SIGN_OUT_URL, - }; - - return communicate(message) - .then((url: string) => { - SPAUtils.setSignOutURL(url, config.clientId, instanceID); - - // Enable OIDC Sessions Management only if it is set to true in the config. - if (config.syncSession) { - checkSession(); - } - - startAutoRefreshToken(); - - return Promise.resolve(response); - }) - .catch(error => { - return Promise.reject(error); - }); - }) - .catch(error => { - return Promise.reject(error); - }); - }; - - const shouldStopAuthn = async (): Promise => { - return await _sessionManagementHelper.receivePromptNoneResponse(async (sessionState: string | null) => { - return setSessionState(sessionState); - }); - }; - - const tryRetrievingUserInfo = async (): Promise => { - if (await isSignedIn()) { - await startAutoRefreshToken(); - - // Enable OIDC Sessions Management only if it is set to true in the config. - if (config.syncSession) { - checkSession(); - } - - return getUser(); - } - - return Promise.resolve(undefined); - }; - - /** - * Initiates the authentication flow. - * - * @returns {Promise} A promise that resolves when authentication is successful. - */ - const signIn = async ( - params?: ExtendedAuthorizeRequestUrlParams, - authorizationCode?: string, - sessionState?: string, - state?: string, - tokenRequestConfig?: { - params: Record; - }, - ): Promise => { - const basicUserInfo = await _authenticationHelper.handleSignIn( - shouldStopAuthn, - checkSession, - tryRetrievingUserInfo, - ); - - if (basicUserInfo) { - return basicUserInfo; - } else { - let resolvedAuthorizationCode: string; - let resolvedSessionState: string; - let resolvedState: string; - - if (config?.responseMode === 'form_post' && authorizationCode) { - resolvedAuthorizationCode = authorizationCode; - resolvedSessionState = sessionState ?? ''; - resolvedState = state ?? ''; - } else { - resolvedAuthorizationCode = - new URL(window.location.href).searchParams.get(OIDCRequestConstants.Params.AUTHORIZATION_CODE) ?? ''; - resolvedSessionState = - new URL(window.location.href).searchParams.get(OIDCRequestConstants.Params.SESSION_STATE) ?? ''; - resolvedState = new URL(window.location.href).searchParams.get(OIDCRequestConstants.Params.STATE) ?? ''; - - SPAUtils.removeAuthorizationCode(); - } - - if (resolvedAuthorizationCode && resolvedState) { - return requestAccessToken(resolvedAuthorizationCode, resolvedSessionState, resolvedState, tokenRequestConfig); - } - - return getSignInUrl(params) - .then(async (response: AuthorizationResponse) => { - location.href = response.authorizationURL; - - await SPAUtils.waitTillPageRedirect(); - - return Promise.resolve({ - allowedScopes: '', - displayName: '', - email: '', - sessionState: '', - sub: '', - tenantDomain: '', - username: '', - }); - }) - .catch(error => { - return Promise.reject(error); - }); - } - }; - - /** - * Initiates the sign out flow. - * - * @returns {Promise} A promise that resolves when sign out is completed. - */ - const signOut = (): Promise => { - return new Promise((resolve, reject) => { - if (!_getSignOutURLFromSessionStorage) { - const message: Message = { - type: SIGN_OUT, - }; - - return communicate(message) - .then(async response => { - window.location.href = response; - - await SPAUtils.waitTillPageRedirect(); - - return resolve(true); - }) - .catch(error => { - return reject(error); - }); - } else { - window.location.href = SPAUtils.getSignOutUrl(config.clientId, instanceID); - - return SPAUtils.waitTillPageRedirect().then(() => { - return Promise.resolve(true); - }); - } - }); - }; - - /** - * Revokes token. - * - * @returns {Promise} A promise that resolves when revoking is completed. - */ - const revokeAccessToken = (): Promise => { - const message: Message = { - type: REVOKE_ACCESS_TOKEN, - }; - - return communicate(message) - .then(response => { - _sessionManagementHelper.reset(); - return Promise.resolve(response); - }) - .catch(error => { - return Promise.reject(error); - }); - }; - - const getOpenIDProviderEndpoints = (): Promise => { - const message: Message = { - type: GET_OIDC_SERVICE_ENDPOINTS, - }; - - return communicate(message) - .then(response => { - return Promise.resolve(response); - }) - .catch(error => { - return Promise.reject(error); - }); - }; - - const getConfigData = (): Promise> => { - const message: Message = { - type: GET_CONFIG_DATA, - }; - - return communicate>(message) - .then(response => { - return Promise.resolve(response); - }) - .catch(error => { - return Promise.reject(error); - }); - }; - - const getUser = (): Promise => { - const message: Message = { - type: GET_BASIC_USER_INFO, - }; - - return communicate(message) - .then(response => { - return Promise.resolve(response); - }) - .catch(error => { - return Promise.reject(error); - }); - }; - - const getDecodedIdToken = (sessionId?: string): Promise => { - const message: Message = { - type: GET_DECODED_ID_TOKEN, - }; - - return communicate(message) - .then(response => { - return Promise.resolve(response); - }) - .catch(error => { - return Promise.reject(error); - }); - }; - - const getDecodedIDPIDToken = (): Promise => { - const message: Message = { - type: GET_DECODED_IDP_ID_TOKEN, - }; - - return communicate(message) - .then(response => { - return Promise.resolve(response); - }) - .catch(error => { - return Promise.reject(error); - }); - }; - - const getCrypto = (): Promise => { - const message: Message = { - type: GET_CRYPTO_HELPER, - }; - - return communicate(message) - .then(response => { - return Promise.resolve(response); - }) - .catch(error => { - return Promise.reject(error); - }); - }; - - const getIdToken = (): Promise => { - const message: Message = { - type: GET_ID_TOKEN, - }; - - return communicate(message) - .then(response => { - return Promise.resolve(response); - }) - .catch(error => { - return Promise.reject(error); - }); - }; - - const isSignedIn = (): Promise => { - const message: Message = { - type: IS_AUTHENTICATED, - }; - - return communicate(message) - .then(response => { - return Promise.resolve(response); - }) - .catch(error => { - return Promise.reject(error); - }); - }; - - const refreshAccessToken = (): Promise => { - const message: Message = { - type: REFRESH_ACCESS_TOKEN, - }; - - return communicate(message); - }; - - const setHttpRequestSuccessCallback = (callback: (response: HttpResponse) => void): void => { - if (callback && typeof callback === 'function') { - httpClientHandlers.requestSuccessCallback = callback; - } - }; - - const setHttpRequestErrorCallback = (callback: (response: HttpError) => void | Promise): void => { - if (callback && typeof callback === 'function') { - httpClientHandlers.requestErrorCallback = callback; - } - }; - - const setHttpRequestStartCallback = (callback: () => void): void => { - if (callback && typeof callback === 'function') { - httpClientHandlers.requestStartCallback = callback; - } - }; - - const setHttpRequestFinishCallback = (callback: () => void): void => { - if (callback && typeof callback === 'function') { - httpClientHandlers.requestFinishCallback = callback; - } - }; - - const reInitialize = async (newConfig: Partial>): Promise => { - const existingConfig = await getConfigData(); - const isCheckSessionIframeDifferent: boolean = !( - existingConfig && - existingConfig.endpoints && - existingConfig.endpoints.checkSessionIframe && - newConfig && - newConfig.endpoints && - newConfig.endpoints.checkSessionIframe && - existingConfig.endpoints.checkSessionIframe === newConfig.endpoints.checkSessionIframe - ); - const config = {...existingConfig, ...newConfig}; - - const message: Message>> = { - data: config, - type: UPDATE_CONFIG, - }; - - await communicate>, void>(message); - - // Re-initiates check session if the check session endpoint is updated. - if (config.syncSession && isCheckSessionIframeDifferent) { - _sessionManagementHelper.reset(); - - checkSession(); - } - }; - - const decodeJwtToken = async >(token: string): Promise => { - return _authenticationClient.decodeJwtToken(token); - }; - - return { - disableHttpHandler, - enableHttpHandler, - getUser, - getConfigData, - getCrypto, - getDecodedIDPIDToken, - getDecodedIdToken, - getIdToken, - getOpenIDProviderEndpoints, - httpRequest, - httpRequestAll, - initialize, - isSignedIn, - refreshAccessToken, - exchangeToken, - revokeAccessToken, - setHttpRequestErrorCallback, - setHttpRequestFinishCallback, - setHttpRequestStartCallback, - setHttpRequestSuccessCallback, - signIn, - signOut, - signInSilently, - reInitialize, - decodeJwtToken - }; -}; +/** + * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { + AsgardeoAuthClient, + AsgardeoAuthException, + AuthClientConfig, + User, + IsomorphicCrypto, + TokenExchangeRequestConfig, + IdToken, + ExtendedAuthorizeRequestUrlParams, + OIDCEndpoints, + OIDCRequestConstants, + Storage, + extractPkceStorageKeyFromState, +} from '@asgardeo/javascript'; +import { + DISABLE_HTTP_HANDLER, + ENABLE_HTTP_HANDLER, + GET_AUTH_URL, + GET_BASIC_USER_INFO, + GET_CONFIG_DATA, + GET_CRYPTO_HELPER, + GET_DECODED_IDP_ID_TOKEN, + GET_DECODED_ID_TOKEN, + GET_ID_TOKEN, + GET_OIDC_SERVICE_ENDPOINTS, + GET_SIGN_OUT_URL, + HTTP_REQUEST, + HTTP_REQUEST_ALL, + INIT, + IS_AUTHENTICATED, + REFRESH_ACCESS_TOKEN, + REQUEST_ACCESS_TOKEN, + REQUEST_CUSTOM_GRANT, + REQUEST_FINISH, + REQUEST_START, + REQUEST_SUCCESS, + REVOKE_ACCESS_TOKEN, + SET_SESSION_STATE, + SIGN_OUT, + SILENT_SIGN_IN_STATE, + START_AUTO_REFRESH_TOKEN, + UPDATE_CONFIG, +} from '../constants'; +import {AuthenticationHelper, SPAHelper, SessionManagementHelper} from '../helpers'; +import { + AuthorizationInfo, + AuthorizationResponse, + HttpClient, + HttpError, + HttpRequestConfig, + HttpResponse, + Message, + ResponseMessage, + WebWorkerClientConfig, + WebWorkerClientInterface, +} from '../models'; +import {SPACustomGrantConfig} from '../models/request-custom-grant'; +import {BrowserStorage} from '../models/storage'; +import {LocalStore, MemoryStore, SessionStore} from '../stores'; +import {SPAUtils} from '../utils'; +import {SPACryptoUtils} from '../utils/crypto-utils'; + +const initiateStore = (store: BrowserStorage | undefined): Storage => { + switch (store) { + case BrowserStorage.LocalStorage: + return new LocalStore(); + case BrowserStorage.SessionStorage: + return new SessionStore(); + case BrowserStorage.BrowserMemory: + return new MemoryStore(); + default: + return new SessionStore(); + } +}; + +export const WebWorkerClient = async ( + instanceID: number, + config: AuthClientConfig, + webWorker: new () => Worker, + getAuthHelper: ( + authClient: AsgardeoAuthClient, + spaHelper: SPAHelper, + ) => AuthenticationHelper, +): Promise => { + /** + * HttpClient handlers + */ + let httpClientHandlers: HttpClient; + /** + * API request time out. + */ + const _requestTimeout: number = config?.requestTimeout ?? 60000; + let _isHttpHandlerEnabled: boolean = true; + let _getSignOutURLFromSessionStorage: boolean = false; + + const _store: Storage = initiateStore(config.storage as BrowserStorage); + const _cryptoUtils: SPACryptoUtils = new SPACryptoUtils(); + const _authenticationClient = new AsgardeoAuthClient(); + await _authenticationClient.initialize(config, _store, _cryptoUtils, instanceID); + const _spaHelper = new SPAHelper(_authenticationClient); + + const _sessionManagementHelper = await SessionManagementHelper( + async () => { + const message: Message = { + type: SIGN_OUT, + }; + + try { + const signOutURL = await communicate(message); + + return signOutURL; + } catch { + return SPAUtils.getSignOutUrl(config.clientId, instanceID); + } + }, + config.storage as BrowserStorage, + (sessionState: string) => setSessionState(sessionState), + ); + + const _authenticationHelper: AuthenticationHelper = getAuthHelper( + _authenticationClient, + _spaHelper, + ); + + const worker: Worker = new webWorker(); + + const communicate = (message: Message): Promise => { + const channel = new MessageChannel(); + + worker.postMessage(message, [channel.port2]); + + return new Promise((resolve, reject) => { + const timer = setTimeout(() => { + reject( + new AsgardeoAuthException( + 'SPA-WEB_WORKER_CLIENT-COM-TO01', + 'Operation timed out.', + 'No response was received from the web worker for ' + + _requestTimeout / 1000 + + ' since dispatching the request', + ), + ); + }, _requestTimeout); + + return (channel.port1.onmessage = ({data}: {data: ResponseMessage}) => { + clearTimeout(timer); + channel.port1.close(); + channel.port2.close(); + + if (data?.success) { + const responseData = data?.data ? JSON.parse(data?.data) : null; + if (data?.blob) { + responseData.data = data?.blob; + } + + resolve(responseData); + } else { + let error = null; + if (data.error) { + try { + error = JSON.parse(data.error); + } catch (parseError) { + // If JSON parsing fails, create a proper error object + error = new AsgardeoAuthException( + 'SPA-WEB_WORKER_CLIENT-COM-PE01', + 'Worker communication error.', + `Failed to parse worker error response: ${data.error}`, + ); + } + } else { + error = new AsgardeoAuthException( + 'SPA-WEB_WORKER_CLIENT-COM-UE01', + 'Unknown worker error.', + 'An unknown error occurred in the web worker.', + ); + } + reject(error); + } + }); + }); + }; + + /** + * Allows using custom grant types. + * + * @param {CustomGrantRequestParams} requestParams Request Parameters. + * + * @returns {Promise} A promise that resolves with a boolean value or the request + * response if the the `returnResponse` attribute in the `requestParams` object is set to `true`. + */ + const exchangeToken = (requestParams: SPACustomGrantConfig): Promise => { + const message: Message = { + data: requestParams, + type: REQUEST_CUSTOM_GRANT, + }; + + return communicate(message) + .then(response => { + if (requestParams.preventSignOutURLUpdate) { + _getSignOutURLFromSessionStorage = true; + } + + return Promise.resolve(response); + }) + .catch(error => { + return Promise.reject(error); + }); + }; + + /** + * + * Send the API request to the web worker and returns the response. + * + * @param {HttpRequestConfig} config The Http Request Config object + * + * @returns {Promise} A promise that resolves with the response data. + */ + const httpRequest = (config: HttpRequestConfig): Promise> => { + /** + * + * Currently FormData is not supported to send to a web worker + * + * Below workaround will represent FormData object as a JSON. + * This workaround will not be needed once FormData object is made cloneable + * Reference: https://github.com/whatwg/xhr/issues/55 + */ + if (config?.data && config?.data instanceof FormData) { + config.data = {...Object.fromEntries((config?.data as any).entries()), formData: true}; + } + + const message: Message = { + data: config, + type: HTTP_REQUEST, + }; + + return communicate>(message) + .then(response => { + return Promise.resolve(response); + }) + .catch(async error => { + if (_isHttpHandlerEnabled) { + if (typeof httpClientHandlers.requestErrorCallback === 'function') { + await httpClientHandlers.requestErrorCallback(error); + } + if (typeof httpClientHandlers.requestFinishCallback === 'function') { + httpClientHandlers.requestFinishCallback(); + } + } + + return Promise.reject(error); + }); + }; + + /** + * + * Send multiple API requests to the web worker and returns the response. + * Similar `axios.spread` in functionality. + * + * @param {HttpRequestConfig[]} configs - The Http Request Config object + * + * @returns {Promise[]>} A promise that resolves with the response data. + */ + const httpRequestAll = (configs: HttpRequestConfig[]): Promise[]> => { + const message: Message = { + data: configs, + type: HTTP_REQUEST_ALL, + }; + + return communicate[]>(message) + .then(response => { + return Promise.resolve(response); + }) + .catch(async error => { + if (_isHttpHandlerEnabled) { + if (typeof httpClientHandlers.requestErrorCallback === 'function') { + await httpClientHandlers.requestErrorCallback(error); + } + if (typeof httpClientHandlers.requestFinishCallback === 'function') { + httpClientHandlers.requestFinishCallback(); + } + } + + return Promise.reject(error); + }); + }; + + const enableHttpHandler = (): Promise => { + const message: Message = { + type: ENABLE_HTTP_HANDLER, + }; + return communicate(message) + .then(() => { + _isHttpHandlerEnabled = true; + + return Promise.resolve(true); + }) + .catch(error => { + return Promise.reject(error); + }); + }; + + const disableHttpHandler = (): Promise => { + const message: Message = { + type: DISABLE_HTTP_HANDLER, + }; + return communicate(message) + .then(() => { + _isHttpHandlerEnabled = false; + + return Promise.resolve(true); + }) + .catch(error => { + return Promise.reject(error); + }); + }; + + /** + * Initializes the object with authentication parameters. + * + * @param {ConfigInterface} config The configuration object. + * + * @returns {Promise} Promise that resolves when initialization is successful. + * + */ + const initialize = (): Promise => { + if (!httpClientHandlers) { + httpClientHandlers = { + requestErrorCallback: () => Promise.resolve(), + requestFinishCallback: () => null, + requestStartCallback: () => null, + requestSuccessCallback: () => null, + }; + } + + worker.onmessage = ({data}) => { + switch (data.type) { + case REQUEST_FINISH: + httpClientHandlers?.requestFinishCallback && httpClientHandlers?.requestFinishCallback(); + break; + case REQUEST_START: + httpClientHandlers?.requestStartCallback && httpClientHandlers?.requestStartCallback(); + break; + case REQUEST_SUCCESS: + httpClientHandlers?.requestSuccessCallback && + httpClientHandlers?.requestSuccessCallback(data.data ? JSON.parse(data.data) : null); + break; + } + }; + + const message: Message & {instanceID: number}> = { + data: {...config, instanceID}, + type: INIT, + }; + + return communicate, null>(message) + .then(() => { + return Promise.resolve(true); + }) + .catch(error => { + return Promise.reject(error); + }); + }; + + const setSessionState = (sessionState: string | null): Promise => { + const message: Message = { + data: sessionState, + type: SET_SESSION_STATE, + }; + + return communicate(message); + }; + + const startAutoRefreshToken = (): Promise => { + const message: Message = { + type: START_AUTO_REFRESH_TOKEN, + }; + + return communicate(message); + }; + + const checkSession = async (): Promise => { + const oidcEndpoints: OIDCEndpoints = await getOpenIDProviderEndpoints(); + const config: AuthClientConfig = await getConfigData(); + + _authenticationHelper.initializeSessionManger( + config, + oidcEndpoints, + async () => (await _authenticationClient.getUserSession()).sessionState, + async (params?: ExtendedAuthorizeRequestUrlParams): Promise => + (await getSignInUrl(params)).authorizationURL, + _sessionManagementHelper, + ); + }; + + const constructSilentSignInUrl = async (additionalParams: Record = {}): Promise => { + const config: AuthClientConfig = await getConfigData(); + const message: Message = { + data: { + prompt: 'none', + state: SILENT_SIGN_IN_STATE, + ...additionalParams, + }, + type: GET_AUTH_URL, + }; + + const response: AuthorizationResponse = await communicate( + message, + ); + + const pkceKey: string = extractPkceStorageKeyFromState( + new URL(response.authorizationURL).searchParams.get(OIDCRequestConstants.Params.STATE) ?? '', + ); + + response.pkce && config.enablePKCE && SPAUtils.setPKCE(pkceKey, response.pkce); + + const urlString: string = response.authorizationURL; + + // Replace form_post with query + const urlObject = new URL(urlString); + urlObject.searchParams.set('response_mode', 'query'); + const url: string = urlObject.toString(); + + return url; + }; + + /** + * This method checks if there is an active user session in the server by sending a prompt none request. + * If the user is signed in, this method sends a token request. Returns false otherwise. + * + * @return {Promise, + tokenRequestConfig?: {params: Record}, + ): Promise => { + return await _authenticationHelper.signInSilently( + constructSilentSignInUrl, + requestAccessToken, + _sessionManagementHelper, + additionalParams, + tokenRequestConfig, + ); + }; + + /** + * Generates an authorization URL. + * + * @param {ExtendedAuthorizeRequestUrlParams} params Authorization URL params. + * @returns {Promise} Authorization URL. + */ + const getSignInUrl = async (params?: ExtendedAuthorizeRequestUrlParams): Promise => { + const config: AuthClientConfig = await getConfigData(); + + const message: Message = { + data: params, + type: GET_AUTH_URL, + }; + + return communicate(message) + .then(async (response: AuthorizationResponse) => { + if (response.pkce && config.enablePKCE) { + const pkceKey: string = extractPkceStorageKeyFromState( + new URL(response.authorizationURL).searchParams.get(OIDCRequestConstants.Params.STATE) ?? '', + ); + + SPAUtils.setPKCE(pkceKey, response.pkce); + } + + return Promise.resolve(response); + }) + .catch(error => { + return Promise.reject(error); + }); + }; + + const requestAccessToken = async ( + resolvedAuthorizationCode: string, + resolvedSessionState: string, + resolvedState: string, + tokenRequestConfig?: { + params: Record; + }, + ): Promise => { + const config: AuthClientConfig = await getConfigData(); + const pkceKey: string = extractPkceStorageKeyFromState(resolvedState); + + const message: Message = { + data: { + code: resolvedAuthorizationCode, + pkce: config.enablePKCE ? SPAUtils.getPKCE(pkceKey) : undefined, + sessionState: resolvedSessionState, + state: resolvedState, + tokenRequestConfig, + }, + type: REQUEST_ACCESS_TOKEN, + }; + + config.enablePKCE && SPAUtils.removePKCE(pkceKey); + + return communicate(message) + .then(response => { + const message: Message = { + type: GET_SIGN_OUT_URL, + }; + + return communicate(message) + .then((url: string) => { + SPAUtils.setSignOutURL(url, config.clientId, instanceID); + + // Enable OIDC Sessions Management only if it is set to true in the config. + if (config.syncSession) { + checkSession(); + } + + startAutoRefreshToken(); + + return Promise.resolve(response); + }) + .catch(error => { + return Promise.reject(error); + }); + }) + .catch(error => { + return Promise.reject(error); + }); + }; + + const shouldStopAuthn = async (): Promise => { + return await _sessionManagementHelper.receivePromptNoneResponse(async (sessionState: string | null) => { + return setSessionState(sessionState); + }); + }; + + const tryRetrievingUserInfo = async (): Promise => { + if (await isSignedIn()) { + await startAutoRefreshToken(); + + // Enable OIDC Sessions Management only if it is set to true in the config. + if (config.syncSession) { + checkSession(); + } + + return getUser(); + } + + return Promise.resolve(undefined); + }; + + /** + * Initiates the authentication flow. + * + * @returns {Promise} A promise that resolves when authentication is successful. + */ + const signIn = async ( + params?: ExtendedAuthorizeRequestUrlParams, + authorizationCode?: string, + sessionState?: string, + state?: string, + tokenRequestConfig?: { + params: Record; + }, + ): Promise => { + const basicUserInfo = await _authenticationHelper.handleSignIn( + shouldStopAuthn, + checkSession, + tryRetrievingUserInfo, + ); + + if (basicUserInfo) { + return basicUserInfo; + } else { + let resolvedAuthorizationCode: string; + let resolvedSessionState: string; + let resolvedState: string; + + if (config?.responseMode === 'form_post' && authorizationCode) { + resolvedAuthorizationCode = authorizationCode; + resolvedSessionState = sessionState ?? ''; + resolvedState = state ?? ''; + } else { + resolvedAuthorizationCode = + new URL(window.location.href).searchParams.get(OIDCRequestConstants.Params.AUTHORIZATION_CODE) ?? ''; + resolvedSessionState = + new URL(window.location.href).searchParams.get(OIDCRequestConstants.Params.SESSION_STATE) ?? ''; + resolvedState = new URL(window.location.href).searchParams.get(OIDCRequestConstants.Params.STATE) ?? ''; + + SPAUtils.removeAuthorizationCode(); + } + + if (resolvedAuthorizationCode && resolvedState) { + return requestAccessToken(resolvedAuthorizationCode, resolvedSessionState, resolvedState, tokenRequestConfig); + } + + return getSignInUrl(params) + .then(async (response: AuthorizationResponse) => { + location.href = response.authorizationURL; + + await SPAUtils.waitTillPageRedirect(); + + return Promise.resolve({ + allowedScopes: '', + displayName: '', + email: '', + sessionState: '', + sub: '', + tenantDomain: '', + username: '', + }); + }) + .catch(error => { + return Promise.reject(error); + }); + } + }; + + /** + * Initiates the sign out flow. + * + * @returns {Promise} A promise that resolves when sign out is completed. + */ + const signOut = (): Promise => { + return new Promise((resolve, reject) => { + if (!_getSignOutURLFromSessionStorage) { + const message: Message = { + type: SIGN_OUT, + }; + + return communicate(message) + .then(async response => { + window.location.href = response; + + await SPAUtils.waitTillPageRedirect(); + + return resolve(true); + }) + .catch(error => { + return reject(error); + }); + } else { + window.location.href = SPAUtils.getSignOutUrl(config.clientId, instanceID); + + return SPAUtils.waitTillPageRedirect().then(() => { + return Promise.resolve(true); + }); + } + }); + }; + + /** + * Revokes token. + * + * @returns {Promise} A promise that resolves when revoking is completed. + */ + const revokeAccessToken = (): Promise => { + const message: Message = { + type: REVOKE_ACCESS_TOKEN, + }; + + return communicate(message) + .then(response => { + _sessionManagementHelper.reset(); + return Promise.resolve(response); + }) + .catch(error => { + return Promise.reject(error); + }); + }; + + const getOpenIDProviderEndpoints = (): Promise => { + const message: Message = { + type: GET_OIDC_SERVICE_ENDPOINTS, + }; + + return communicate(message) + .then(response => { + return Promise.resolve(response); + }) + .catch(error => { + return Promise.reject(error); + }); + }; + + const getConfigData = (): Promise> => { + const message: Message = { + type: GET_CONFIG_DATA, + }; + + return communicate>(message) + .then(response => { + return Promise.resolve(response); + }) + .catch(error => { + return Promise.reject(error); + }); + }; + + const getUser = (): Promise => { + const message: Message = { + type: GET_BASIC_USER_INFO, + }; + + return communicate(message) + .then(response => { + return Promise.resolve(response); + }) + .catch(error => { + return Promise.reject(error); + }); + }; + + const getDecodedIdToken = (sessionId?: string): Promise => { + const message: Message = { + type: GET_DECODED_ID_TOKEN, + }; + + return communicate(message) + .then(response => { + return Promise.resolve(response); + }) + .catch(error => { + return Promise.reject(error); + }); + }; + + const getDecodedIDPIDToken = (): Promise => { + const message: Message = { + type: GET_DECODED_IDP_ID_TOKEN, + }; + + return communicate(message) + .then(response => { + return Promise.resolve(response); + }) + .catch(error => { + return Promise.reject(error); + }); + }; + + const getCrypto = (): Promise => { + const message: Message = { + type: GET_CRYPTO_HELPER, + }; + + return communicate(message) + .then(response => { + return Promise.resolve(response); + }) + .catch(error => { + return Promise.reject(error); + }); + }; + + const getIdToken = (): Promise => { + const message: Message = { + type: GET_ID_TOKEN, + }; + + return communicate(message) + .then(response => { + return Promise.resolve(response); + }) + .catch(error => { + return Promise.reject(error); + }); + }; + + const isSignedIn = (): Promise => { + const message: Message = { + type: IS_AUTHENTICATED, + }; + + return communicate(message) + .then(response => { + return Promise.resolve(response); + }) + .catch(error => { + return Promise.reject(error); + }); + }; + + const refreshAccessToken = (): Promise => { + const message: Message = { + type: REFRESH_ACCESS_TOKEN, + }; + + return communicate(message); + }; + + const setHttpRequestSuccessCallback = (callback: (response: HttpResponse) => void): void => { + if (callback && typeof callback === 'function') { + httpClientHandlers.requestSuccessCallback = callback; + } + }; + + const setHttpRequestErrorCallback = (callback: (response: HttpError) => void | Promise): void => { + if (callback && typeof callback === 'function') { + httpClientHandlers.requestErrorCallback = callback; + } + }; + + const setHttpRequestStartCallback = (callback: () => void): void => { + if (callback && typeof callback === 'function') { + httpClientHandlers.requestStartCallback = callback; + } + }; + + const setHttpRequestFinishCallback = (callback: () => void): void => { + if (callback && typeof callback === 'function') { + httpClientHandlers.requestFinishCallback = callback; + } + }; + + const reInitialize = async (newConfig: Partial>): Promise => { + const existingConfig = await getConfigData(); + const isCheckSessionIframeDifferent: boolean = !( + existingConfig && + existingConfig.endpoints && + existingConfig.endpoints.checkSessionIframe && + newConfig && + newConfig.endpoints && + newConfig.endpoints.checkSessionIframe && + existingConfig.endpoints.checkSessionIframe === newConfig.endpoints.checkSessionIframe + ); + const config = {...existingConfig, ...newConfig}; + + const message: Message>> = { + data: config, + type: UPDATE_CONFIG, + }; + + await communicate>, void>(message); + + // Re-initiates check session if the check session endpoint is updated. + if (config.syncSession && isCheckSessionIframeDifferent) { + _sessionManagementHelper.reset(); + + checkSession(); + } + }; + + const decodeJwtToken = async >(token: string): Promise => { + return _authenticationClient.decodeJwtToken(token); + }; + + return { + disableHttpHandler, + enableHttpHandler, + getUser, + getConfigData, + getCrypto, + getDecodedIDPIDToken, + getDecodedIdToken, + getIdToken, + getOpenIDProviderEndpoints, + httpRequest, + httpRequestAll, + initialize, + isSignedIn, + refreshAccessToken, + exchangeToken, + revokeAccessToken, + setHttpRequestErrorCallback, + setHttpRequestFinishCallback, + setHttpRequestStartCallback, + setHttpRequestSuccessCallback, + signIn, + signOut, + signInSilently, + reInitialize, + decodeJwtToken + }; +}; diff --git a/packages/browser/src/__legacy__/http-client/clients/axios-http-client.ts b/packages/browser/src/__legacy__/http-client/clients/axios-http-client.ts index 9ce419166..29ce5cb11 100755 --- a/packages/browser/src/__legacy__/http-client/clients/axios-http-client.ts +++ b/packages/browser/src/__legacy__/http-client/clients/axios-http-client.ts @@ -116,6 +116,23 @@ export class HttpClient implements HttpClientInterface, - getAuthHelper: ( - authClient: AsgardeoAuthClient, - spaHelper: SPAHelper, - ) => AuthenticationHelper, -): Promise => { - const _store: Storage = new MemoryStore(); - const _cryptoUtils: SPACryptoUtils = new SPACryptoUtils(); - const _authenticationClient = new AsgardeoAuthClient(); - await _authenticationClient.initialize(config, _store, _cryptoUtils); - - const _spaHelper = new SPAHelper(_authenticationClient); - - const _authenticationHelper: AuthenticationHelper = getAuthHelper( - _authenticationClient, - _spaHelper, - ); - - const _dataLayer = _authenticationClient.getStorageManager(); - - const _httpClient: HttpClientInstance = HttpClient.getInstance(); - - const attachToken = async (request: HttpRequestConfig): Promise => { - await _authenticationHelper.attachTokenToRequestConfig(request); - }; - - _httpClient?.init && (await _httpClient.init(true, attachToken)); - - const setHttpRequestStartCallback = (callback: () => void): void => { - _httpClient?.setHttpRequestStartCallback && _httpClient.setHttpRequestStartCallback(callback); - }; - - const setHttpRequestSuccessCallback = (callback: (response: HttpResponse) => void): void => { - _httpClient?.setHttpRequestSuccessCallback && _httpClient.setHttpRequestSuccessCallback(callback); - }; - - const setHttpRequestFinishCallback = (callback: () => void): void => { - _httpClient?.setHttpRequestFinishCallback && _httpClient.setHttpRequestFinishCallback(callback); - }; - - const httpRequest = async (requestConfig: HttpRequestConfig): Promise => { - return await _authenticationHelper.httpRequest(_httpClient, requestConfig); - }; - - const httpRequestAll = async (requestConfigs: HttpRequestConfig[]): Promise => { - return await _authenticationHelper.httpRequestAll(requestConfigs, _httpClient); - }; - - const enableHttpHandler = (): void => { - _authenticationHelper.enableHttpHandler(_httpClient); - }; - - const disableHttpHandler = (): void => { - _authenticationHelper.disableHttpHandler(_httpClient); - }; - - const getSignInUrl = async (params?: AuthorizeRequestUrlParams): Promise => { - return _authenticationClient - .getSignInUrl(params) - .then(async (url: string) => { - const urlObject: URL = new URL(url); - const state: string = urlObject.searchParams.get(OIDCRequestConstants.Params.STATE) ?? ''; - const pkce: string = await _authenticationClient.getPKCECode(state); - - return {authorizationURL: url, pkce: pkce}; - }) - .catch(error => Promise.reject(error)); - }; - - const startAutoRefreshToken = async (): Promise => { - _spaHelper.clearRefreshTokenTimeout(); - _spaHelper.refreshAccessTokenAutomatically(_authenticationHelper); - - return; - }; - - const requestAccessToken = async ( - authorizationCode?: string, - sessionState?: string, - pkce?: string, - state?: string, - ): Promise => { - return await _authenticationHelper.requestAccessToken(authorizationCode, sessionState, undefined, pkce, state); - }; - - const signOut = async (): Promise => { - _spaHelper.clearRefreshTokenTimeout(); - - return await _authenticationClient.getSignOutUrl(); - }; - - const getSignOutUrl = async (): Promise => { - return await _authenticationClient.getSignOutUrl(); - }; - - const exchangeToken = async (config: TokenExchangeRequestConfig): Promise => { - return await _authenticationHelper.exchangeToken(config); - }; - - const refreshAccessToken = async (): Promise => { - try { - return await _authenticationHelper.refreshAccessToken(); - } catch (error) { - return Promise.reject(error); - } - }; - - const revokeAccessToken = async (): Promise => { - const timer: number = await _spaHelper.getRefreshTimeoutTimer(); - - return _authenticationClient - .revokeAccessToken() - .then(() => { - _spaHelper.clearRefreshTokenTimeout(timer); - - return Promise.resolve(true); - }) - .catch(error => Promise.reject(error)); - }; - - const getUser = async (): Promise => { - return _authenticationHelper.getUser(); - }; - - const getDecodedIdToken = async (sessionId?: string): Promise => { - return _authenticationHelper.getDecodedIdToken(sessionId); - }; - - const getCrypto = async (): Promise => { - return _authenticationHelper.getCrypto(); - }; - - const getDecodedIDPIDToken = async (): Promise => { - return _authenticationHelper.getDecodedIDPIDToken(); - }; - - const getIdToken = async (): Promise => { - return _authenticationHelper.getIdToken(); - }; - const getOpenIDProviderEndpoints = async (): Promise => { - return _authenticationHelper.getOpenIDProviderEndpoints(); - }; - - const getAccessToken = (): Promise => { - return _authenticationHelper.getAccessToken(); - }; - - const isSignedIn = (): Promise => { - return _authenticationHelper.isSignedIn(); - }; - - const setSessionState = async (sessionState: string): Promise => { - await _dataLayer.setSessionDataParameter( - OIDCRequestConstants.Params.SESSION_STATE as keyof SessionData, - sessionState, - ); - - return; - }; - - const reInitialize = async (config: Partial>): Promise => { - await _authenticationClient.reInitialize(config); - - return; - }; - - const getConfigData = async (): Promise> => { - return _dataLayer.getConfigData(); - }; - - return { - disableHttpHandler, - enableHttpHandler, - getAccessToken, - getSignInUrl, - getUser, - getConfigData, - getCrypto, - getDecodedIDPIDToken, - getDecodedIdToken, - getIdToken, - getOpenIDProviderEndpoints, - getSignOutUrl, - httpRequest, - httpRequestAll, - isSignedIn, - refreshAccessToken, - requestAccessToken, - exchangeToken, - revokeAccessToken, - setHttpRequestFinishCallback, - setHttpRequestStartCallback, - setHttpRequestSuccessCallback, - setSessionState, - signOut, - startAutoRefreshToken, - reInitialize, - }; -}; +/** + * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { + AsgardeoAuthClient, + AuthClientConfig, + AuthorizeRequestUrlParams, + User, + IsomorphicCrypto, + TokenExchangeRequestConfig, + IdToken, + OIDCEndpoints, + OIDCRequestConstants, + SessionData, + Storage, +} from '@asgardeo/javascript'; +import {AuthenticationHelper, SPAHelper} from '../helpers'; +import {HttpClient, HttpClientInstance} from '../http-client'; +import { + AuthorizationResponse, + HttpRequestConfig, + HttpResponse, + WebWorkerClientConfig, + WebWorkerCoreInterface, +} from '../models'; +import {MemoryStore} from '../stores'; +import {SPACryptoUtils} from '../utils/crypto-utils'; + +export const WebWorkerCore = async ( + instanceID: number, + config: AuthClientConfig, + getAuthHelper: ( + authClient: AsgardeoAuthClient, + spaHelper: SPAHelper, + ) => AuthenticationHelper, +): Promise => { + const _store: Storage = new MemoryStore(); + const _cryptoUtils: SPACryptoUtils = new SPACryptoUtils(); + const _authenticationClient = new AsgardeoAuthClient(); + await _authenticationClient.initialize(config, _store, _cryptoUtils, instanceID); + + const _spaHelper = new SPAHelper(_authenticationClient); + + const _authenticationHelper: AuthenticationHelper = getAuthHelper( + _authenticationClient, + _spaHelper, + ); + + const _dataLayer = _authenticationClient.getStorageManager(); + + const _httpClient: HttpClientInstance = HttpClient.getInstance(instanceID); + + const attachToken = async (request: HttpRequestConfig): Promise => { + await _authenticationHelper.attachTokenToRequestConfig(request); + }; + + _httpClient?.init && (await _httpClient.init(true, attachToken)); + + const setHttpRequestStartCallback = (callback: () => void): void => { + _httpClient?.setHttpRequestStartCallback && _httpClient.setHttpRequestStartCallback(callback); + }; + + const setHttpRequestSuccessCallback = (callback: (response: HttpResponse) => void): void => { + _httpClient?.setHttpRequestSuccessCallback && _httpClient.setHttpRequestSuccessCallback(callback); + }; + + const setHttpRequestFinishCallback = (callback: () => void): void => { + _httpClient?.setHttpRequestFinishCallback && _httpClient.setHttpRequestFinishCallback(callback); + }; + + const httpRequest = async (requestConfig: HttpRequestConfig): Promise => { + return await _authenticationHelper.httpRequest(_httpClient, requestConfig); + }; + + const httpRequestAll = async (requestConfigs: HttpRequestConfig[]): Promise => { + return await _authenticationHelper.httpRequestAll(requestConfigs, _httpClient); + }; + + const enableHttpHandler = (): void => { + _authenticationHelper.enableHttpHandler(_httpClient); + }; + + const disableHttpHandler = (): void => { + _authenticationHelper.disableHttpHandler(_httpClient); + }; + + const getSignInUrl = async (params?: AuthorizeRequestUrlParams): Promise => { + return _authenticationClient + .getSignInUrl(params) + .then(async (url: string) => { + const urlObject: URL = new URL(url); + const state: string = urlObject.searchParams.get(OIDCRequestConstants.Params.STATE) ?? ''; + const pkce: string = await _authenticationClient.getPKCECode(state); + + return {authorizationURL: url, pkce: pkce}; + }) + .catch(error => Promise.reject(error)); + }; + + const startAutoRefreshToken = async (): Promise => { + _spaHelper.clearRefreshTokenTimeout(); + _spaHelper.refreshAccessTokenAutomatically(_authenticationHelper); + + return; + }; + + const requestAccessToken = async ( + authorizationCode?: string, + sessionState?: string, + pkce?: string, + state?: string, + ): Promise => { + return await _authenticationHelper.requestAccessToken(authorizationCode, sessionState, undefined, pkce, state); + }; + + const signOut = async (): Promise => { + _spaHelper.clearRefreshTokenTimeout(); + + return await _authenticationClient.getSignOutUrl(); + }; + + const getSignOutUrl = async (): Promise => { + return await _authenticationClient.getSignOutUrl(); + }; + + const exchangeToken = async (config: TokenExchangeRequestConfig): Promise => { + return await _authenticationHelper.exchangeToken(config); + }; + + const refreshAccessToken = async (): Promise => { + try { + return await _authenticationHelper.refreshAccessToken(); + } catch (error) { + return Promise.reject(error); + } + }; + + const revokeAccessToken = async (): Promise => { + const timer: number = await _spaHelper.getRefreshTimeoutTimer(); + + return _authenticationClient + .revokeAccessToken() + .then(() => { + _spaHelper.clearRefreshTokenTimeout(timer); + + return Promise.resolve(true); + }) + .catch(error => Promise.reject(error)); + }; + + const getUser = async (): Promise => { + return _authenticationHelper.getUser(); + }; + + const getDecodedIdToken = async (sessionId?: string): Promise => { + return _authenticationHelper.getDecodedIdToken(sessionId); + }; + + const getCrypto = async (): Promise => { + return _authenticationHelper.getCrypto(); + }; + + const getDecodedIDPIDToken = async (): Promise => { + return _authenticationHelper.getDecodedIDPIDToken(); + }; + + const getIdToken = async (): Promise => { + return _authenticationHelper.getIdToken(); + }; + const getOpenIDProviderEndpoints = async (): Promise => { + return _authenticationHelper.getOpenIDProviderEndpoints(); + }; + + const getAccessToken = (): Promise => { + return _authenticationHelper.getAccessToken(); + }; + + const isSignedIn = (): Promise => { + return _authenticationHelper.isSignedIn(); + }; + + const setSessionState = async (sessionState: string): Promise => { + await _dataLayer.setSessionDataParameter( + OIDCRequestConstants.Params.SESSION_STATE as keyof SessionData, + sessionState, + ); + + return; + }; + + const reInitialize = async (config: Partial>): Promise => { + await _authenticationClient.reInitialize(config); + + return; + }; + + const getConfigData = async (): Promise> => { + return _dataLayer.getConfigData(); + }; + + return { + disableHttpHandler, + enableHttpHandler, + getAccessToken, + getSignInUrl, + getUser, + getConfigData, + getCrypto, + getDecodedIDPIDToken, + getDecodedIdToken, + getIdToken, + getOpenIDProviderEndpoints, + getSignOutUrl, + httpRequest, + httpRequestAll, + isSignedIn, + refreshAccessToken, + requestAccessToken, + exchangeToken, + revokeAccessToken, + setHttpRequestFinishCallback, + setHttpRequestStartCallback, + setHttpRequestSuccessCallback, + setSessionState, + signOut, + startAutoRefreshToken, + reInitialize, + }; +}; diff --git a/packages/browser/src/__legacy__/worker/worker-receiver.ts b/packages/browser/src/__legacy__/worker/worker-receiver.ts index ee7c8deff..8631242ac 100644 --- a/packages/browser/src/__legacy__/worker/worker-receiver.ts +++ b/packages/browser/src/__legacy__/worker/worker-receiver.ts @@ -1,314 +1,315 @@ -/** - * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import {AsgardeoAuthClient, AsgardeoAuthException, AuthClientConfig, User} from '@asgardeo/javascript'; -import {WebWorkerCore} from './worker-core'; -import { - DISABLE_HTTP_HANDLER, - ENABLE_HTTP_HANDLER, - GET_AUTH_URL, - GET_BASIC_USER_INFO, - GET_CONFIG_DATA, - GET_CRYPTO_HELPER, - GET_DECODED_ID_TOKEN, - GET_ID_TOKEN, - GET_OIDC_SERVICE_ENDPOINTS, - GET_SIGN_OUT_URL, - HTTP_REQUEST, - HTTP_REQUEST_ALL, - INIT, - IS_AUTHENTICATED, - REFRESH_ACCESS_TOKEN, - REQUEST_ACCESS_TOKEN, - REQUEST_CUSTOM_GRANT, - REQUEST_FINISH, - REQUEST_START, - REQUEST_SUCCESS, - REVOKE_ACCESS_TOKEN, - SET_SESSION_STATE, - SIGN_OUT, - START_AUTO_REFRESH_TOKEN, - UPDATE_CONFIG, -} from '../constants'; -import {AuthenticationHelper, SPAHelper} from '../helpers'; -import { - AuthorizationResponse, - HttpResponse, - WebWorkerClass, - WebWorkerClientConfig, - WebWorkerCoreInterface, -} from '../models'; -import {MessageUtils} from '../utils'; - -export const workerReceiver = ( - getAuthHelper: ( - authClient: AsgardeoAuthClient, - spaHelper: SPAHelper, - ) => AuthenticationHelper, -) => { - const ctx: WebWorkerClass = self as any; - - let webWorker: WebWorkerCoreInterface; - - ctx.onmessage = async ({data, ports}) => { - const port = ports[0]; - if (data.type !== INIT && !webWorker) { - port.postMessage( - MessageUtils.generateFailureMessage( - new AsgardeoAuthException( - 'SPA-CLIENT_WORKER-ONMSG-NF01', - 'The web worker has not been initialized yet.', - 'The initialize method needs to be called before the specified operation can be carried out.', - ), - ), - ); - - return; - } - - switch (data.type) { - case INIT: - try { - const config: AuthClientConfig = {...data.data}; - webWorker = await WebWorkerCore(config, getAuthHelper); - webWorker.setHttpRequestFinishCallback(onRequestFinishCallback); - webWorker.setHttpRequestStartCallback(onRequestStartCallback); - webWorker.setHttpRequestSuccessCallback(onRequestSuccessCallback); - port.postMessage(MessageUtils.generateSuccessMessage()); - } catch (error) { - port.postMessage(MessageUtils.generateFailureMessage(error)); - } - - break; - case GET_AUTH_URL: - webWorker - .getSignInUrl(data?.data) - .then((response: AuthorizationResponse) => { - port.postMessage(MessageUtils.generateSuccessMessage(response)); - }) - .catch(error => { - port.postMessage(MessageUtils.generateFailureMessage(error)); - }); - - break; - case REQUEST_ACCESS_TOKEN: - webWorker - .requestAccessToken(data?.data?.code, data?.data?.sessionState, data?.data?.pkce, data?.data?.state) - .then((response: User) => { - port.postMessage(MessageUtils.generateSuccessMessage(response)); - }) - .catch(error => { - port.postMessage(MessageUtils.generateFailureMessage(error)); - }); - - break; - case HTTP_REQUEST: { - const request = data.data; - const requestData = request?.data; - if (data.data?.data?.formData === true) { - const formData = new FormData(); - for (const key in requestData) { - if (key === 'formData') { - continue; - } - formData.append(key, requestData[key]); - } - request.data = formData; - } - webWorker - .httpRequest(request) - .then(response => { - port.postMessage(MessageUtils.generateSuccessMessage(response)); - }) - .catch(error => { - port.postMessage(MessageUtils.generateFailureMessage(error)); - }); - - break; - } - case HTTP_REQUEST_ALL: - webWorker - .httpRequestAll(data.data) - .then(response => { - port.postMessage(MessageUtils.generateSuccessMessage(response)); - }) - .catch(error => { - port.postMessage(MessageUtils.generateFailureMessage(error)); - }); - - break; - case SIGN_OUT: - try { - port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.signOut())); - } catch (error) { - port.postMessage(MessageUtils.generateFailureMessage(error)); - } - - break; - case REQUEST_CUSTOM_GRANT: - webWorker - .exchangeToken(data.data) - .then(response => { - port.postMessage(MessageUtils.generateSuccessMessage(response)); - }) - .catch(error => { - port.postMessage(MessageUtils.generateFailureMessage(error)); - }); - - break; - case REVOKE_ACCESS_TOKEN: - webWorker - .revokeAccessToken() - .then(response => { - port.postMessage(MessageUtils.generateSuccessMessage(response)); - }) - .catch(error => { - port.postMessage(MessageUtils.generateFailureMessage(error)); - }); - break; - case GET_OIDC_SERVICE_ENDPOINTS: - try { - port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.getOpenIDProviderEndpoints())); - } catch (error) { - port.postMessage(MessageUtils.generateFailureMessage(error)); - } - - break; - case GET_BASIC_USER_INFO: - try { - port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.getUser())); - } catch (error) { - port.postMessage(MessageUtils.generateFailureMessage(error)); - } - - break; - case GET_DECODED_ID_TOKEN: - try { - port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.getDecodedIdToken())); - } catch (error) { - port.postMessage(MessageUtils.generateFailureMessage(error)); - } - - break; - case GET_CRYPTO_HELPER: - try { - port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.getCrypto())); - } catch (error) { - port.postMessage(MessageUtils.generateFailureMessage(error)); - } - - break; - case GET_ID_TOKEN: - try { - port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.getIdToken())); - } catch (error) { - port.postMessage(MessageUtils.generateFailureMessage(error)); - } - - break; - case ENABLE_HTTP_HANDLER: - webWorker.enableHttpHandler(); - port.postMessage(MessageUtils.generateSuccessMessage()); - - break; - case DISABLE_HTTP_HANDLER: - webWorker.disableHttpHandler(); - port.postMessage(MessageUtils.generateSuccessMessage()); - - break; - case IS_AUTHENTICATED: - try { - port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.isSignedIn())); - } catch (error) { - port.postMessage(MessageUtils.generateFailureMessage(error)); - } - - break; - case GET_SIGN_OUT_URL: - try { - port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.getSignOutUrl())); - } catch (error) { - port.postMessage(MessageUtils.generateFailureMessage(error)); - } - - break; - case REFRESH_ACCESS_TOKEN: - try { - port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.refreshAccessToken())); - } catch (error) { - port.postMessage(MessageUtils.generateFailureMessage(error)); - } - - break; - case START_AUTO_REFRESH_TOKEN: - try { - port.postMessage(MessageUtils.generateSuccessMessage(webWorker.startAutoRefreshToken())); - } catch (error) { - port.postMessage(MessageUtils.generateFailureMessage(error)); - } - - break; - case SET_SESSION_STATE: - try { - port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.setSessionState(data?.data))); - } catch (error) { - port.postMessage(MessageUtils.generateFailureMessage(error)); - } - - break; - case UPDATE_CONFIG: - try { - port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.reInitialize(data?.data))); - } catch (error) { - port.postMessage(MessageUtils.generateFailureMessage(error)); - } - - break; - case GET_CONFIG_DATA: - try { - port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.getConfigData())); - } catch (error) { - port.postMessage(MessageUtils.generateFailureMessage(error)); - } - - break; - default: - port?.postMessage( - MessageUtils.generateFailureMessage( - new AsgardeoAuthException( - 'SPA-CLIENT_WORKER-ONMSG-IV02', - 'The message type is invalid.', - `The message type provided, ${data.type}, is invalid.`, - ), - ), - ); - } - }; - - const onRequestStartCallback = (): void => { - ctx.postMessage({type: REQUEST_START}); - }; - - const onRequestSuccessCallback = (response: HttpResponse): void => { - ctx.postMessage({data: JSON.stringify(response ?? ''), type: REQUEST_SUCCESS}); - }; - - const onRequestFinishCallback = (): void => { - ctx.postMessage({type: REQUEST_FINISH}); - }; -}; +/** + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import {AsgardeoAuthClient, AsgardeoAuthException, AuthClientConfig, User} from '@asgardeo/javascript'; +import {WebWorkerCore} from './worker-core'; +import { + DISABLE_HTTP_HANDLER, + ENABLE_HTTP_HANDLER, + GET_AUTH_URL, + GET_BASIC_USER_INFO, + GET_CONFIG_DATA, + GET_CRYPTO_HELPER, + GET_DECODED_ID_TOKEN, + GET_ID_TOKEN, + GET_OIDC_SERVICE_ENDPOINTS, + GET_SIGN_OUT_URL, + HTTP_REQUEST, + HTTP_REQUEST_ALL, + INIT, + IS_AUTHENTICATED, + REFRESH_ACCESS_TOKEN, + REQUEST_ACCESS_TOKEN, + REQUEST_CUSTOM_GRANT, + REQUEST_FINISH, + REQUEST_START, + REQUEST_SUCCESS, + REVOKE_ACCESS_TOKEN, + SET_SESSION_STATE, + SIGN_OUT, + START_AUTO_REFRESH_TOKEN, + UPDATE_CONFIG, +} from '../constants'; +import {AuthenticationHelper, SPAHelper} from '../helpers'; +import { + AuthorizationResponse, + HttpResponse, + WebWorkerClass, + WebWorkerClientConfig, + WebWorkerCoreInterface, +} from '../models'; +import {MessageUtils} from '../utils'; + +export const workerReceiver = ( + getAuthHelper: ( + authClient: AsgardeoAuthClient, + spaHelper: SPAHelper, + ) => AuthenticationHelper, +) => { + const ctx: WebWorkerClass = self as any; + + let webWorker: WebWorkerCoreInterface; + + ctx.onmessage = async ({data, ports}) => { + const port = ports[0]; + if (data.type !== INIT && !webWorker) { + port.postMessage( + MessageUtils.generateFailureMessage( + new AsgardeoAuthException( + 'SPA-CLIENT_WORKER-ONMSG-NF01', + 'The web worker has not been initialized yet.', + 'The initialize method needs to be called before the specified operation can be carried out.', + ), + ), + ); + + return; + } + + switch (data.type) { + case INIT: + try { + const {instanceID = 0, ...configData} = data.data; + const config: AuthClientConfig = {...configData}; + webWorker = await WebWorkerCore(instanceID, config, getAuthHelper); + webWorker.setHttpRequestFinishCallback(onRequestFinishCallback); + webWorker.setHttpRequestStartCallback(onRequestStartCallback); + webWorker.setHttpRequestSuccessCallback(onRequestSuccessCallback); + port.postMessage(MessageUtils.generateSuccessMessage()); + } catch (error) { + port.postMessage(MessageUtils.generateFailureMessage(error)); + } + + break; + case GET_AUTH_URL: + webWorker + .getSignInUrl(data?.data) + .then((response: AuthorizationResponse) => { + port.postMessage(MessageUtils.generateSuccessMessage(response)); + }) + .catch(error => { + port.postMessage(MessageUtils.generateFailureMessage(error)); + }); + + break; + case REQUEST_ACCESS_TOKEN: + webWorker + .requestAccessToken(data?.data?.code, data?.data?.sessionState, data?.data?.pkce, data?.data?.state) + .then((response: User) => { + port.postMessage(MessageUtils.generateSuccessMessage(response)); + }) + .catch(error => { + port.postMessage(MessageUtils.generateFailureMessage(error)); + }); + + break; + case HTTP_REQUEST: { + const request = data.data; + const requestData = request?.data; + if (data.data?.data?.formData === true) { + const formData = new FormData(); + for (const key in requestData) { + if (key === 'formData') { + continue; + } + formData.append(key, requestData[key]); + } + request.data = formData; + } + webWorker + .httpRequest(request) + .then(response => { + port.postMessage(MessageUtils.generateSuccessMessage(response)); + }) + .catch(error => { + port.postMessage(MessageUtils.generateFailureMessage(error)); + }); + + break; + } + case HTTP_REQUEST_ALL: + webWorker + .httpRequestAll(data.data) + .then(response => { + port.postMessage(MessageUtils.generateSuccessMessage(response)); + }) + .catch(error => { + port.postMessage(MessageUtils.generateFailureMessage(error)); + }); + + break; + case SIGN_OUT: + try { + port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.signOut())); + } catch (error) { + port.postMessage(MessageUtils.generateFailureMessage(error)); + } + + break; + case REQUEST_CUSTOM_GRANT: + webWorker + .exchangeToken(data.data) + .then(response => { + port.postMessage(MessageUtils.generateSuccessMessage(response)); + }) + .catch(error => { + port.postMessage(MessageUtils.generateFailureMessage(error)); + }); + + break; + case REVOKE_ACCESS_TOKEN: + webWorker + .revokeAccessToken() + .then(response => { + port.postMessage(MessageUtils.generateSuccessMessage(response)); + }) + .catch(error => { + port.postMessage(MessageUtils.generateFailureMessage(error)); + }); + break; + case GET_OIDC_SERVICE_ENDPOINTS: + try { + port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.getOpenIDProviderEndpoints())); + } catch (error) { + port.postMessage(MessageUtils.generateFailureMessage(error)); + } + + break; + case GET_BASIC_USER_INFO: + try { + port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.getUser())); + } catch (error) { + port.postMessage(MessageUtils.generateFailureMessage(error)); + } + + break; + case GET_DECODED_ID_TOKEN: + try { + port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.getDecodedIdToken())); + } catch (error) { + port.postMessage(MessageUtils.generateFailureMessage(error)); + } + + break; + case GET_CRYPTO_HELPER: + try { + port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.getCrypto())); + } catch (error) { + port.postMessage(MessageUtils.generateFailureMessage(error)); + } + + break; + case GET_ID_TOKEN: + try { + port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.getIdToken())); + } catch (error) { + port.postMessage(MessageUtils.generateFailureMessage(error)); + } + + break; + case ENABLE_HTTP_HANDLER: + webWorker.enableHttpHandler(); + port.postMessage(MessageUtils.generateSuccessMessage()); + + break; + case DISABLE_HTTP_HANDLER: + webWorker.disableHttpHandler(); + port.postMessage(MessageUtils.generateSuccessMessage()); + + break; + case IS_AUTHENTICATED: + try { + port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.isSignedIn())); + } catch (error) { + port.postMessage(MessageUtils.generateFailureMessage(error)); + } + + break; + case GET_SIGN_OUT_URL: + try { + port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.getSignOutUrl())); + } catch (error) { + port.postMessage(MessageUtils.generateFailureMessage(error)); + } + + break; + case REFRESH_ACCESS_TOKEN: + try { + port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.refreshAccessToken())); + } catch (error) { + port.postMessage(MessageUtils.generateFailureMessage(error)); + } + + break; + case START_AUTO_REFRESH_TOKEN: + try { + port.postMessage(MessageUtils.generateSuccessMessage(webWorker.startAutoRefreshToken())); + } catch (error) { + port.postMessage(MessageUtils.generateFailureMessage(error)); + } + + break; + case SET_SESSION_STATE: + try { + port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.setSessionState(data?.data))); + } catch (error) { + port.postMessage(MessageUtils.generateFailureMessage(error)); + } + + break; + case UPDATE_CONFIG: + try { + port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.reInitialize(data?.data))); + } catch (error) { + port.postMessage(MessageUtils.generateFailureMessage(error)); + } + + break; + case GET_CONFIG_DATA: + try { + port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.getConfigData())); + } catch (error) { + port.postMessage(MessageUtils.generateFailureMessage(error)); + } + + break; + default: + port?.postMessage( + MessageUtils.generateFailureMessage( + new AsgardeoAuthException( + 'SPA-CLIENT_WORKER-ONMSG-IV02', + 'The message type is invalid.', + `The message type provided, ${data.type}, is invalid.`, + ), + ), + ); + } + }; + + const onRequestStartCallback = (): void => { + ctx.postMessage({type: REQUEST_START}); + }; + + const onRequestSuccessCallback = (response: HttpResponse): void => { + ctx.postMessage({data: JSON.stringify(response ?? ''), type: REQUEST_SUCCESS}); + }; + + const onRequestFinishCallback = (): void => { + ctx.postMessage({type: REQUEST_FINISH}); + }; +}; From 8571623f6cbc1b344e3b0e1ea76134b7ef9b1d5b Mon Sep 17 00:00:00 2001 From: kavindadewmith Date: Tue, 24 Feb 2026 11:08:52 +0530 Subject: [PATCH 6/6] Refactor web-worker files to change endoflines to lf --- .../__legacy__/clients/web-worker-client.ts | 1782 ++++++++--------- .../src/__legacy__/worker/worker-core.ts | 488 ++--- .../src/__legacy__/worker/worker-receiver.ts | 630 +++--- 3 files changed, 1450 insertions(+), 1450 deletions(-) diff --git a/packages/browser/src/__legacy__/clients/web-worker-client.ts b/packages/browser/src/__legacy__/clients/web-worker-client.ts index 99856e4be..0ce4518d1 100755 --- a/packages/browser/src/__legacy__/clients/web-worker-client.ts +++ b/packages/browser/src/__legacy__/clients/web-worker-client.ts @@ -1,891 +1,891 @@ -/** - * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { - AsgardeoAuthClient, - AsgardeoAuthException, - AuthClientConfig, - User, - IsomorphicCrypto, - TokenExchangeRequestConfig, - IdToken, - ExtendedAuthorizeRequestUrlParams, - OIDCEndpoints, - OIDCRequestConstants, - Storage, - extractPkceStorageKeyFromState, -} from '@asgardeo/javascript'; -import { - DISABLE_HTTP_HANDLER, - ENABLE_HTTP_HANDLER, - GET_AUTH_URL, - GET_BASIC_USER_INFO, - GET_CONFIG_DATA, - GET_CRYPTO_HELPER, - GET_DECODED_IDP_ID_TOKEN, - GET_DECODED_ID_TOKEN, - GET_ID_TOKEN, - GET_OIDC_SERVICE_ENDPOINTS, - GET_SIGN_OUT_URL, - HTTP_REQUEST, - HTTP_REQUEST_ALL, - INIT, - IS_AUTHENTICATED, - REFRESH_ACCESS_TOKEN, - REQUEST_ACCESS_TOKEN, - REQUEST_CUSTOM_GRANT, - REQUEST_FINISH, - REQUEST_START, - REQUEST_SUCCESS, - REVOKE_ACCESS_TOKEN, - SET_SESSION_STATE, - SIGN_OUT, - SILENT_SIGN_IN_STATE, - START_AUTO_REFRESH_TOKEN, - UPDATE_CONFIG, -} from '../constants'; -import {AuthenticationHelper, SPAHelper, SessionManagementHelper} from '../helpers'; -import { - AuthorizationInfo, - AuthorizationResponse, - HttpClient, - HttpError, - HttpRequestConfig, - HttpResponse, - Message, - ResponseMessage, - WebWorkerClientConfig, - WebWorkerClientInterface, -} from '../models'; -import {SPACustomGrantConfig} from '../models/request-custom-grant'; -import {BrowserStorage} from '../models/storage'; -import {LocalStore, MemoryStore, SessionStore} from '../stores'; -import {SPAUtils} from '../utils'; -import {SPACryptoUtils} from '../utils/crypto-utils'; - -const initiateStore = (store: BrowserStorage | undefined): Storage => { - switch (store) { - case BrowserStorage.LocalStorage: - return new LocalStore(); - case BrowserStorage.SessionStorage: - return new SessionStore(); - case BrowserStorage.BrowserMemory: - return new MemoryStore(); - default: - return new SessionStore(); - } -}; - -export const WebWorkerClient = async ( - instanceID: number, - config: AuthClientConfig, - webWorker: new () => Worker, - getAuthHelper: ( - authClient: AsgardeoAuthClient, - spaHelper: SPAHelper, - ) => AuthenticationHelper, -): Promise => { - /** - * HttpClient handlers - */ - let httpClientHandlers: HttpClient; - /** - * API request time out. - */ - const _requestTimeout: number = config?.requestTimeout ?? 60000; - let _isHttpHandlerEnabled: boolean = true; - let _getSignOutURLFromSessionStorage: boolean = false; - - const _store: Storage = initiateStore(config.storage as BrowserStorage); - const _cryptoUtils: SPACryptoUtils = new SPACryptoUtils(); - const _authenticationClient = new AsgardeoAuthClient(); - await _authenticationClient.initialize(config, _store, _cryptoUtils, instanceID); - const _spaHelper = new SPAHelper(_authenticationClient); - - const _sessionManagementHelper = await SessionManagementHelper( - async () => { - const message: Message = { - type: SIGN_OUT, - }; - - try { - const signOutURL = await communicate(message); - - return signOutURL; - } catch { - return SPAUtils.getSignOutUrl(config.clientId, instanceID); - } - }, - config.storage as BrowserStorage, - (sessionState: string) => setSessionState(sessionState), - ); - - const _authenticationHelper: AuthenticationHelper = getAuthHelper( - _authenticationClient, - _spaHelper, - ); - - const worker: Worker = new webWorker(); - - const communicate = (message: Message): Promise => { - const channel = new MessageChannel(); - - worker.postMessage(message, [channel.port2]); - - return new Promise((resolve, reject) => { - const timer = setTimeout(() => { - reject( - new AsgardeoAuthException( - 'SPA-WEB_WORKER_CLIENT-COM-TO01', - 'Operation timed out.', - 'No response was received from the web worker for ' + - _requestTimeout / 1000 + - ' since dispatching the request', - ), - ); - }, _requestTimeout); - - return (channel.port1.onmessage = ({data}: {data: ResponseMessage}) => { - clearTimeout(timer); - channel.port1.close(); - channel.port2.close(); - - if (data?.success) { - const responseData = data?.data ? JSON.parse(data?.data) : null; - if (data?.blob) { - responseData.data = data?.blob; - } - - resolve(responseData); - } else { - let error = null; - if (data.error) { - try { - error = JSON.parse(data.error); - } catch (parseError) { - // If JSON parsing fails, create a proper error object - error = new AsgardeoAuthException( - 'SPA-WEB_WORKER_CLIENT-COM-PE01', - 'Worker communication error.', - `Failed to parse worker error response: ${data.error}`, - ); - } - } else { - error = new AsgardeoAuthException( - 'SPA-WEB_WORKER_CLIENT-COM-UE01', - 'Unknown worker error.', - 'An unknown error occurred in the web worker.', - ); - } - reject(error); - } - }); - }); - }; - - /** - * Allows using custom grant types. - * - * @param {CustomGrantRequestParams} requestParams Request Parameters. - * - * @returns {Promise} A promise that resolves with a boolean value or the request - * response if the the `returnResponse` attribute in the `requestParams` object is set to `true`. - */ - const exchangeToken = (requestParams: SPACustomGrantConfig): Promise => { - const message: Message = { - data: requestParams, - type: REQUEST_CUSTOM_GRANT, - }; - - return communicate(message) - .then(response => { - if (requestParams.preventSignOutURLUpdate) { - _getSignOutURLFromSessionStorage = true; - } - - return Promise.resolve(response); - }) - .catch(error => { - return Promise.reject(error); - }); - }; - - /** - * - * Send the API request to the web worker and returns the response. - * - * @param {HttpRequestConfig} config The Http Request Config object - * - * @returns {Promise} A promise that resolves with the response data. - */ - const httpRequest = (config: HttpRequestConfig): Promise> => { - /** - * - * Currently FormData is not supported to send to a web worker - * - * Below workaround will represent FormData object as a JSON. - * This workaround will not be needed once FormData object is made cloneable - * Reference: https://github.com/whatwg/xhr/issues/55 - */ - if (config?.data && config?.data instanceof FormData) { - config.data = {...Object.fromEntries((config?.data as any).entries()), formData: true}; - } - - const message: Message = { - data: config, - type: HTTP_REQUEST, - }; - - return communicate>(message) - .then(response => { - return Promise.resolve(response); - }) - .catch(async error => { - if (_isHttpHandlerEnabled) { - if (typeof httpClientHandlers.requestErrorCallback === 'function') { - await httpClientHandlers.requestErrorCallback(error); - } - if (typeof httpClientHandlers.requestFinishCallback === 'function') { - httpClientHandlers.requestFinishCallback(); - } - } - - return Promise.reject(error); - }); - }; - - /** - * - * Send multiple API requests to the web worker and returns the response. - * Similar `axios.spread` in functionality. - * - * @param {HttpRequestConfig[]} configs - The Http Request Config object - * - * @returns {Promise[]>} A promise that resolves with the response data. - */ - const httpRequestAll = (configs: HttpRequestConfig[]): Promise[]> => { - const message: Message = { - data: configs, - type: HTTP_REQUEST_ALL, - }; - - return communicate[]>(message) - .then(response => { - return Promise.resolve(response); - }) - .catch(async error => { - if (_isHttpHandlerEnabled) { - if (typeof httpClientHandlers.requestErrorCallback === 'function') { - await httpClientHandlers.requestErrorCallback(error); - } - if (typeof httpClientHandlers.requestFinishCallback === 'function') { - httpClientHandlers.requestFinishCallback(); - } - } - - return Promise.reject(error); - }); - }; - - const enableHttpHandler = (): Promise => { - const message: Message = { - type: ENABLE_HTTP_HANDLER, - }; - return communicate(message) - .then(() => { - _isHttpHandlerEnabled = true; - - return Promise.resolve(true); - }) - .catch(error => { - return Promise.reject(error); - }); - }; - - const disableHttpHandler = (): Promise => { - const message: Message = { - type: DISABLE_HTTP_HANDLER, - }; - return communicate(message) - .then(() => { - _isHttpHandlerEnabled = false; - - return Promise.resolve(true); - }) - .catch(error => { - return Promise.reject(error); - }); - }; - - /** - * Initializes the object with authentication parameters. - * - * @param {ConfigInterface} config The configuration object. - * - * @returns {Promise} Promise that resolves when initialization is successful. - * - */ - const initialize = (): Promise => { - if (!httpClientHandlers) { - httpClientHandlers = { - requestErrorCallback: () => Promise.resolve(), - requestFinishCallback: () => null, - requestStartCallback: () => null, - requestSuccessCallback: () => null, - }; - } - - worker.onmessage = ({data}) => { - switch (data.type) { - case REQUEST_FINISH: - httpClientHandlers?.requestFinishCallback && httpClientHandlers?.requestFinishCallback(); - break; - case REQUEST_START: - httpClientHandlers?.requestStartCallback && httpClientHandlers?.requestStartCallback(); - break; - case REQUEST_SUCCESS: - httpClientHandlers?.requestSuccessCallback && - httpClientHandlers?.requestSuccessCallback(data.data ? JSON.parse(data.data) : null); - break; - } - }; - - const message: Message & {instanceID: number}> = { - data: {...config, instanceID}, - type: INIT, - }; - - return communicate, null>(message) - .then(() => { - return Promise.resolve(true); - }) - .catch(error => { - return Promise.reject(error); - }); - }; - - const setSessionState = (sessionState: string | null): Promise => { - const message: Message = { - data: sessionState, - type: SET_SESSION_STATE, - }; - - return communicate(message); - }; - - const startAutoRefreshToken = (): Promise => { - const message: Message = { - type: START_AUTO_REFRESH_TOKEN, - }; - - return communicate(message); - }; - - const checkSession = async (): Promise => { - const oidcEndpoints: OIDCEndpoints = await getOpenIDProviderEndpoints(); - const config: AuthClientConfig = await getConfigData(); - - _authenticationHelper.initializeSessionManger( - config, - oidcEndpoints, - async () => (await _authenticationClient.getUserSession()).sessionState, - async (params?: ExtendedAuthorizeRequestUrlParams): Promise => - (await getSignInUrl(params)).authorizationURL, - _sessionManagementHelper, - ); - }; - - const constructSilentSignInUrl = async (additionalParams: Record = {}): Promise => { - const config: AuthClientConfig = await getConfigData(); - const message: Message = { - data: { - prompt: 'none', - state: SILENT_SIGN_IN_STATE, - ...additionalParams, - }, - type: GET_AUTH_URL, - }; - - const response: AuthorizationResponse = await communicate( - message, - ); - - const pkceKey: string = extractPkceStorageKeyFromState( - new URL(response.authorizationURL).searchParams.get(OIDCRequestConstants.Params.STATE) ?? '', - ); - - response.pkce && config.enablePKCE && SPAUtils.setPKCE(pkceKey, response.pkce); - - const urlString: string = response.authorizationURL; - - // Replace form_post with query - const urlObject = new URL(urlString); - urlObject.searchParams.set('response_mode', 'query'); - const url: string = urlObject.toString(); - - return url; - }; - - /** - * This method checks if there is an active user session in the server by sending a prompt none request. - * If the user is signed in, this method sends a token request. Returns false otherwise. - * - * @return {Promise, - tokenRequestConfig?: {params: Record}, - ): Promise => { - return await _authenticationHelper.signInSilently( - constructSilentSignInUrl, - requestAccessToken, - _sessionManagementHelper, - additionalParams, - tokenRequestConfig, - ); - }; - - /** - * Generates an authorization URL. - * - * @param {ExtendedAuthorizeRequestUrlParams} params Authorization URL params. - * @returns {Promise} Authorization URL. - */ - const getSignInUrl = async (params?: ExtendedAuthorizeRequestUrlParams): Promise => { - const config: AuthClientConfig = await getConfigData(); - - const message: Message = { - data: params, - type: GET_AUTH_URL, - }; - - return communicate(message) - .then(async (response: AuthorizationResponse) => { - if (response.pkce && config.enablePKCE) { - const pkceKey: string = extractPkceStorageKeyFromState( - new URL(response.authorizationURL).searchParams.get(OIDCRequestConstants.Params.STATE) ?? '', - ); - - SPAUtils.setPKCE(pkceKey, response.pkce); - } - - return Promise.resolve(response); - }) - .catch(error => { - return Promise.reject(error); - }); - }; - - const requestAccessToken = async ( - resolvedAuthorizationCode: string, - resolvedSessionState: string, - resolvedState: string, - tokenRequestConfig?: { - params: Record; - }, - ): Promise => { - const config: AuthClientConfig = await getConfigData(); - const pkceKey: string = extractPkceStorageKeyFromState(resolvedState); - - const message: Message = { - data: { - code: resolvedAuthorizationCode, - pkce: config.enablePKCE ? SPAUtils.getPKCE(pkceKey) : undefined, - sessionState: resolvedSessionState, - state: resolvedState, - tokenRequestConfig, - }, - type: REQUEST_ACCESS_TOKEN, - }; - - config.enablePKCE && SPAUtils.removePKCE(pkceKey); - - return communicate(message) - .then(response => { - const message: Message = { - type: GET_SIGN_OUT_URL, - }; - - return communicate(message) - .then((url: string) => { - SPAUtils.setSignOutURL(url, config.clientId, instanceID); - - // Enable OIDC Sessions Management only if it is set to true in the config. - if (config.syncSession) { - checkSession(); - } - - startAutoRefreshToken(); - - return Promise.resolve(response); - }) - .catch(error => { - return Promise.reject(error); - }); - }) - .catch(error => { - return Promise.reject(error); - }); - }; - - const shouldStopAuthn = async (): Promise => { - return await _sessionManagementHelper.receivePromptNoneResponse(async (sessionState: string | null) => { - return setSessionState(sessionState); - }); - }; - - const tryRetrievingUserInfo = async (): Promise => { - if (await isSignedIn()) { - await startAutoRefreshToken(); - - // Enable OIDC Sessions Management only if it is set to true in the config. - if (config.syncSession) { - checkSession(); - } - - return getUser(); - } - - return Promise.resolve(undefined); - }; - - /** - * Initiates the authentication flow. - * - * @returns {Promise} A promise that resolves when authentication is successful. - */ - const signIn = async ( - params?: ExtendedAuthorizeRequestUrlParams, - authorizationCode?: string, - sessionState?: string, - state?: string, - tokenRequestConfig?: { - params: Record; - }, - ): Promise => { - const basicUserInfo = await _authenticationHelper.handleSignIn( - shouldStopAuthn, - checkSession, - tryRetrievingUserInfo, - ); - - if (basicUserInfo) { - return basicUserInfo; - } else { - let resolvedAuthorizationCode: string; - let resolvedSessionState: string; - let resolvedState: string; - - if (config?.responseMode === 'form_post' && authorizationCode) { - resolvedAuthorizationCode = authorizationCode; - resolvedSessionState = sessionState ?? ''; - resolvedState = state ?? ''; - } else { - resolvedAuthorizationCode = - new URL(window.location.href).searchParams.get(OIDCRequestConstants.Params.AUTHORIZATION_CODE) ?? ''; - resolvedSessionState = - new URL(window.location.href).searchParams.get(OIDCRequestConstants.Params.SESSION_STATE) ?? ''; - resolvedState = new URL(window.location.href).searchParams.get(OIDCRequestConstants.Params.STATE) ?? ''; - - SPAUtils.removeAuthorizationCode(); - } - - if (resolvedAuthorizationCode && resolvedState) { - return requestAccessToken(resolvedAuthorizationCode, resolvedSessionState, resolvedState, tokenRequestConfig); - } - - return getSignInUrl(params) - .then(async (response: AuthorizationResponse) => { - location.href = response.authorizationURL; - - await SPAUtils.waitTillPageRedirect(); - - return Promise.resolve({ - allowedScopes: '', - displayName: '', - email: '', - sessionState: '', - sub: '', - tenantDomain: '', - username: '', - }); - }) - .catch(error => { - return Promise.reject(error); - }); - } - }; - - /** - * Initiates the sign out flow. - * - * @returns {Promise} A promise that resolves when sign out is completed. - */ - const signOut = (): Promise => { - return new Promise((resolve, reject) => { - if (!_getSignOutURLFromSessionStorage) { - const message: Message = { - type: SIGN_OUT, - }; - - return communicate(message) - .then(async response => { - window.location.href = response; - - await SPAUtils.waitTillPageRedirect(); - - return resolve(true); - }) - .catch(error => { - return reject(error); - }); - } else { - window.location.href = SPAUtils.getSignOutUrl(config.clientId, instanceID); - - return SPAUtils.waitTillPageRedirect().then(() => { - return Promise.resolve(true); - }); - } - }); - }; - - /** - * Revokes token. - * - * @returns {Promise} A promise that resolves when revoking is completed. - */ - const revokeAccessToken = (): Promise => { - const message: Message = { - type: REVOKE_ACCESS_TOKEN, - }; - - return communicate(message) - .then(response => { - _sessionManagementHelper.reset(); - return Promise.resolve(response); - }) - .catch(error => { - return Promise.reject(error); - }); - }; - - const getOpenIDProviderEndpoints = (): Promise => { - const message: Message = { - type: GET_OIDC_SERVICE_ENDPOINTS, - }; - - return communicate(message) - .then(response => { - return Promise.resolve(response); - }) - .catch(error => { - return Promise.reject(error); - }); - }; - - const getConfigData = (): Promise> => { - const message: Message = { - type: GET_CONFIG_DATA, - }; - - return communicate>(message) - .then(response => { - return Promise.resolve(response); - }) - .catch(error => { - return Promise.reject(error); - }); - }; - - const getUser = (): Promise => { - const message: Message = { - type: GET_BASIC_USER_INFO, - }; - - return communicate(message) - .then(response => { - return Promise.resolve(response); - }) - .catch(error => { - return Promise.reject(error); - }); - }; - - const getDecodedIdToken = (sessionId?: string): Promise => { - const message: Message = { - type: GET_DECODED_ID_TOKEN, - }; - - return communicate(message) - .then(response => { - return Promise.resolve(response); - }) - .catch(error => { - return Promise.reject(error); - }); - }; - - const getDecodedIDPIDToken = (): Promise => { - const message: Message = { - type: GET_DECODED_IDP_ID_TOKEN, - }; - - return communicate(message) - .then(response => { - return Promise.resolve(response); - }) - .catch(error => { - return Promise.reject(error); - }); - }; - - const getCrypto = (): Promise => { - const message: Message = { - type: GET_CRYPTO_HELPER, - }; - - return communicate(message) - .then(response => { - return Promise.resolve(response); - }) - .catch(error => { - return Promise.reject(error); - }); - }; - - const getIdToken = (): Promise => { - const message: Message = { - type: GET_ID_TOKEN, - }; - - return communicate(message) - .then(response => { - return Promise.resolve(response); - }) - .catch(error => { - return Promise.reject(error); - }); - }; - - const isSignedIn = (): Promise => { - const message: Message = { - type: IS_AUTHENTICATED, - }; - - return communicate(message) - .then(response => { - return Promise.resolve(response); - }) - .catch(error => { - return Promise.reject(error); - }); - }; - - const refreshAccessToken = (): Promise => { - const message: Message = { - type: REFRESH_ACCESS_TOKEN, - }; - - return communicate(message); - }; - - const setHttpRequestSuccessCallback = (callback: (response: HttpResponse) => void): void => { - if (callback && typeof callback === 'function') { - httpClientHandlers.requestSuccessCallback = callback; - } - }; - - const setHttpRequestErrorCallback = (callback: (response: HttpError) => void | Promise): void => { - if (callback && typeof callback === 'function') { - httpClientHandlers.requestErrorCallback = callback; - } - }; - - const setHttpRequestStartCallback = (callback: () => void): void => { - if (callback && typeof callback === 'function') { - httpClientHandlers.requestStartCallback = callback; - } - }; - - const setHttpRequestFinishCallback = (callback: () => void): void => { - if (callback && typeof callback === 'function') { - httpClientHandlers.requestFinishCallback = callback; - } - }; - - const reInitialize = async (newConfig: Partial>): Promise => { - const existingConfig = await getConfigData(); - const isCheckSessionIframeDifferent: boolean = !( - existingConfig && - existingConfig.endpoints && - existingConfig.endpoints.checkSessionIframe && - newConfig && - newConfig.endpoints && - newConfig.endpoints.checkSessionIframe && - existingConfig.endpoints.checkSessionIframe === newConfig.endpoints.checkSessionIframe - ); - const config = {...existingConfig, ...newConfig}; - - const message: Message>> = { - data: config, - type: UPDATE_CONFIG, - }; - - await communicate>, void>(message); - - // Re-initiates check session if the check session endpoint is updated. - if (config.syncSession && isCheckSessionIframeDifferent) { - _sessionManagementHelper.reset(); - - checkSession(); - } - }; - - const decodeJwtToken = async >(token: string): Promise => { - return _authenticationClient.decodeJwtToken(token); - }; - - return { - disableHttpHandler, - enableHttpHandler, - getUser, - getConfigData, - getCrypto, - getDecodedIDPIDToken, - getDecodedIdToken, - getIdToken, - getOpenIDProviderEndpoints, - httpRequest, - httpRequestAll, - initialize, - isSignedIn, - refreshAccessToken, - exchangeToken, - revokeAccessToken, - setHttpRequestErrorCallback, - setHttpRequestFinishCallback, - setHttpRequestStartCallback, - setHttpRequestSuccessCallback, - signIn, - signOut, - signInSilently, - reInitialize, - decodeJwtToken - }; -}; +/** + * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { + AsgardeoAuthClient, + AsgardeoAuthException, + AuthClientConfig, + User, + IsomorphicCrypto, + TokenExchangeRequestConfig, + IdToken, + ExtendedAuthorizeRequestUrlParams, + OIDCEndpoints, + OIDCRequestConstants, + Storage, + extractPkceStorageKeyFromState, +} from '@asgardeo/javascript'; +import { + DISABLE_HTTP_HANDLER, + ENABLE_HTTP_HANDLER, + GET_AUTH_URL, + GET_BASIC_USER_INFO, + GET_CONFIG_DATA, + GET_CRYPTO_HELPER, + GET_DECODED_IDP_ID_TOKEN, + GET_DECODED_ID_TOKEN, + GET_ID_TOKEN, + GET_OIDC_SERVICE_ENDPOINTS, + GET_SIGN_OUT_URL, + HTTP_REQUEST, + HTTP_REQUEST_ALL, + INIT, + IS_AUTHENTICATED, + REFRESH_ACCESS_TOKEN, + REQUEST_ACCESS_TOKEN, + REQUEST_CUSTOM_GRANT, + REQUEST_FINISH, + REQUEST_START, + REQUEST_SUCCESS, + REVOKE_ACCESS_TOKEN, + SET_SESSION_STATE, + SIGN_OUT, + SILENT_SIGN_IN_STATE, + START_AUTO_REFRESH_TOKEN, + UPDATE_CONFIG, +} from '../constants'; +import {AuthenticationHelper, SPAHelper, SessionManagementHelper} from '../helpers'; +import { + AuthorizationInfo, + AuthorizationResponse, + HttpClient, + HttpError, + HttpRequestConfig, + HttpResponse, + Message, + ResponseMessage, + WebWorkerClientConfig, + WebWorkerClientInterface, +} from '../models'; +import {SPACustomGrantConfig} from '../models/request-custom-grant'; +import {BrowserStorage} from '../models/storage'; +import {LocalStore, MemoryStore, SessionStore} from '../stores'; +import {SPAUtils} from '../utils'; +import {SPACryptoUtils} from '../utils/crypto-utils'; + +const initiateStore = (store: BrowserStorage | undefined): Storage => { + switch (store) { + case BrowserStorage.LocalStorage: + return new LocalStore(); + case BrowserStorage.SessionStorage: + return new SessionStore(); + case BrowserStorage.BrowserMemory: + return new MemoryStore(); + default: + return new SessionStore(); + } +}; + +export const WebWorkerClient = async ( + instanceID: number, + config: AuthClientConfig, + webWorker: new () => Worker, + getAuthHelper: ( + authClient: AsgardeoAuthClient, + spaHelper: SPAHelper, + ) => AuthenticationHelper, +): Promise => { + /** + * HttpClient handlers + */ + let httpClientHandlers: HttpClient; + /** + * API request time out. + */ + const _requestTimeout: number = config?.requestTimeout ?? 60000; + let _isHttpHandlerEnabled: boolean = true; + let _getSignOutURLFromSessionStorage: boolean = false; + + const _store: Storage = initiateStore(config.storage as BrowserStorage); + const _cryptoUtils: SPACryptoUtils = new SPACryptoUtils(); + const _authenticationClient = new AsgardeoAuthClient(); + await _authenticationClient.initialize(config, _store, _cryptoUtils, instanceID); + const _spaHelper = new SPAHelper(_authenticationClient); + + const _sessionManagementHelper = await SessionManagementHelper( + async () => { + const message: Message = { + type: SIGN_OUT, + }; + + try { + const signOutURL = await communicate(message); + + return signOutURL; + } catch { + return SPAUtils.getSignOutUrl(config.clientId, instanceID); + } + }, + config.storage as BrowserStorage, + (sessionState: string) => setSessionState(sessionState), + ); + + const _authenticationHelper: AuthenticationHelper = getAuthHelper( + _authenticationClient, + _spaHelper, + ); + + const worker: Worker = new webWorker(); + + const communicate = (message: Message): Promise => { + const channel = new MessageChannel(); + + worker.postMessage(message, [channel.port2]); + + return new Promise((resolve, reject) => { + const timer = setTimeout(() => { + reject( + new AsgardeoAuthException( + 'SPA-WEB_WORKER_CLIENT-COM-TO01', + 'Operation timed out.', + 'No response was received from the web worker for ' + + _requestTimeout / 1000 + + ' since dispatching the request', + ), + ); + }, _requestTimeout); + + return (channel.port1.onmessage = ({data}: {data: ResponseMessage}) => { + clearTimeout(timer); + channel.port1.close(); + channel.port2.close(); + + if (data?.success) { + const responseData = data?.data ? JSON.parse(data?.data) : null; + if (data?.blob) { + responseData.data = data?.blob; + } + + resolve(responseData); + } else { + let error = null; + if (data.error) { + try { + error = JSON.parse(data.error); + } catch (parseError) { + // If JSON parsing fails, create a proper error object + error = new AsgardeoAuthException( + 'SPA-WEB_WORKER_CLIENT-COM-PE01', + 'Worker communication error.', + `Failed to parse worker error response: ${data.error}`, + ); + } + } else { + error = new AsgardeoAuthException( + 'SPA-WEB_WORKER_CLIENT-COM-UE01', + 'Unknown worker error.', + 'An unknown error occurred in the web worker.', + ); + } + reject(error); + } + }); + }); + }; + + /** + * Allows using custom grant types. + * + * @param {CustomGrantRequestParams} requestParams Request Parameters. + * + * @returns {Promise} A promise that resolves with a boolean value or the request + * response if the the `returnResponse` attribute in the `requestParams` object is set to `true`. + */ + const exchangeToken = (requestParams: SPACustomGrantConfig): Promise => { + const message: Message = { + data: requestParams, + type: REQUEST_CUSTOM_GRANT, + }; + + return communicate(message) + .then(response => { + if (requestParams.preventSignOutURLUpdate) { + _getSignOutURLFromSessionStorage = true; + } + + return Promise.resolve(response); + }) + .catch(error => { + return Promise.reject(error); + }); + }; + + /** + * + * Send the API request to the web worker and returns the response. + * + * @param {HttpRequestConfig} config The Http Request Config object + * + * @returns {Promise} A promise that resolves with the response data. + */ + const httpRequest = (config: HttpRequestConfig): Promise> => { + /** + * + * Currently FormData is not supported to send to a web worker + * + * Below workaround will represent FormData object as a JSON. + * This workaround will not be needed once FormData object is made cloneable + * Reference: https://github.com/whatwg/xhr/issues/55 + */ + if (config?.data && config?.data instanceof FormData) { + config.data = {...Object.fromEntries((config?.data as any).entries()), formData: true}; + } + + const message: Message = { + data: config, + type: HTTP_REQUEST, + }; + + return communicate>(message) + .then(response => { + return Promise.resolve(response); + }) + .catch(async error => { + if (_isHttpHandlerEnabled) { + if (typeof httpClientHandlers.requestErrorCallback === 'function') { + await httpClientHandlers.requestErrorCallback(error); + } + if (typeof httpClientHandlers.requestFinishCallback === 'function') { + httpClientHandlers.requestFinishCallback(); + } + } + + return Promise.reject(error); + }); + }; + + /** + * + * Send multiple API requests to the web worker and returns the response. + * Similar `axios.spread` in functionality. + * + * @param {HttpRequestConfig[]} configs - The Http Request Config object + * + * @returns {Promise[]>} A promise that resolves with the response data. + */ + const httpRequestAll = (configs: HttpRequestConfig[]): Promise[]> => { + const message: Message = { + data: configs, + type: HTTP_REQUEST_ALL, + }; + + return communicate[]>(message) + .then(response => { + return Promise.resolve(response); + }) + .catch(async error => { + if (_isHttpHandlerEnabled) { + if (typeof httpClientHandlers.requestErrorCallback === 'function') { + await httpClientHandlers.requestErrorCallback(error); + } + if (typeof httpClientHandlers.requestFinishCallback === 'function') { + httpClientHandlers.requestFinishCallback(); + } + } + + return Promise.reject(error); + }); + }; + + const enableHttpHandler = (): Promise => { + const message: Message = { + type: ENABLE_HTTP_HANDLER, + }; + return communicate(message) + .then(() => { + _isHttpHandlerEnabled = true; + + return Promise.resolve(true); + }) + .catch(error => { + return Promise.reject(error); + }); + }; + + const disableHttpHandler = (): Promise => { + const message: Message = { + type: DISABLE_HTTP_HANDLER, + }; + return communicate(message) + .then(() => { + _isHttpHandlerEnabled = false; + + return Promise.resolve(true); + }) + .catch(error => { + return Promise.reject(error); + }); + }; + + /** + * Initializes the object with authentication parameters. + * + * @param {ConfigInterface} config The configuration object. + * + * @returns {Promise} Promise that resolves when initialization is successful. + * + */ + const initialize = (): Promise => { + if (!httpClientHandlers) { + httpClientHandlers = { + requestErrorCallback: () => Promise.resolve(), + requestFinishCallback: () => null, + requestStartCallback: () => null, + requestSuccessCallback: () => null, + }; + } + + worker.onmessage = ({data}) => { + switch (data.type) { + case REQUEST_FINISH: + httpClientHandlers?.requestFinishCallback && httpClientHandlers?.requestFinishCallback(); + break; + case REQUEST_START: + httpClientHandlers?.requestStartCallback && httpClientHandlers?.requestStartCallback(); + break; + case REQUEST_SUCCESS: + httpClientHandlers?.requestSuccessCallback && + httpClientHandlers?.requestSuccessCallback(data.data ? JSON.parse(data.data) : null); + break; + } + }; + + const message: Message & {instanceID: number}> = { + data: {...config, instanceID}, + type: INIT, + }; + + return communicate, null>(message) + .then(() => { + return Promise.resolve(true); + }) + .catch(error => { + return Promise.reject(error); + }); + }; + + const setSessionState = (sessionState: string | null): Promise => { + const message: Message = { + data: sessionState, + type: SET_SESSION_STATE, + }; + + return communicate(message); + }; + + const startAutoRefreshToken = (): Promise => { + const message: Message = { + type: START_AUTO_REFRESH_TOKEN, + }; + + return communicate(message); + }; + + const checkSession = async (): Promise => { + const oidcEndpoints: OIDCEndpoints = await getOpenIDProviderEndpoints(); + const config: AuthClientConfig = await getConfigData(); + + _authenticationHelper.initializeSessionManger( + config, + oidcEndpoints, + async () => (await _authenticationClient.getUserSession()).sessionState, + async (params?: ExtendedAuthorizeRequestUrlParams): Promise => + (await getSignInUrl(params)).authorizationURL, + _sessionManagementHelper, + ); + }; + + const constructSilentSignInUrl = async (additionalParams: Record = {}): Promise => { + const config: AuthClientConfig = await getConfigData(); + const message: Message = { + data: { + prompt: 'none', + state: SILENT_SIGN_IN_STATE, + ...additionalParams, + }, + type: GET_AUTH_URL, + }; + + const response: AuthorizationResponse = await communicate( + message, + ); + + const pkceKey: string = extractPkceStorageKeyFromState( + new URL(response.authorizationURL).searchParams.get(OIDCRequestConstants.Params.STATE) ?? '', + ); + + response.pkce && config.enablePKCE && SPAUtils.setPKCE(pkceKey, response.pkce); + + const urlString: string = response.authorizationURL; + + // Replace form_post with query + const urlObject = new URL(urlString); + urlObject.searchParams.set('response_mode', 'query'); + const url: string = urlObject.toString(); + + return url; + }; + + /** + * This method checks if there is an active user session in the server by sending a prompt none request. + * If the user is signed in, this method sends a token request. Returns false otherwise. + * + * @return {Promise, + tokenRequestConfig?: {params: Record}, + ): Promise => { + return await _authenticationHelper.signInSilently( + constructSilentSignInUrl, + requestAccessToken, + _sessionManagementHelper, + additionalParams, + tokenRequestConfig, + ); + }; + + /** + * Generates an authorization URL. + * + * @param {ExtendedAuthorizeRequestUrlParams} params Authorization URL params. + * @returns {Promise} Authorization URL. + */ + const getSignInUrl = async (params?: ExtendedAuthorizeRequestUrlParams): Promise => { + const config: AuthClientConfig = await getConfigData(); + + const message: Message = { + data: params, + type: GET_AUTH_URL, + }; + + return communicate(message) + .then(async (response: AuthorizationResponse) => { + if (response.pkce && config.enablePKCE) { + const pkceKey: string = extractPkceStorageKeyFromState( + new URL(response.authorizationURL).searchParams.get(OIDCRequestConstants.Params.STATE) ?? '', + ); + + SPAUtils.setPKCE(pkceKey, response.pkce); + } + + return Promise.resolve(response); + }) + .catch(error => { + return Promise.reject(error); + }); + }; + + const requestAccessToken = async ( + resolvedAuthorizationCode: string, + resolvedSessionState: string, + resolvedState: string, + tokenRequestConfig?: { + params: Record; + }, + ): Promise => { + const config: AuthClientConfig = await getConfigData(); + const pkceKey: string = extractPkceStorageKeyFromState(resolvedState); + + const message: Message = { + data: { + code: resolvedAuthorizationCode, + pkce: config.enablePKCE ? SPAUtils.getPKCE(pkceKey) : undefined, + sessionState: resolvedSessionState, + state: resolvedState, + tokenRequestConfig, + }, + type: REQUEST_ACCESS_TOKEN, + }; + + config.enablePKCE && SPAUtils.removePKCE(pkceKey); + + return communicate(message) + .then(response => { + const message: Message = { + type: GET_SIGN_OUT_URL, + }; + + return communicate(message) + .then((url: string) => { + SPAUtils.setSignOutURL(url, config.clientId, instanceID); + + // Enable OIDC Sessions Management only if it is set to true in the config. + if (config.syncSession) { + checkSession(); + } + + startAutoRefreshToken(); + + return Promise.resolve(response); + }) + .catch(error => { + return Promise.reject(error); + }); + }) + .catch(error => { + return Promise.reject(error); + }); + }; + + const shouldStopAuthn = async (): Promise => { + return await _sessionManagementHelper.receivePromptNoneResponse(async (sessionState: string | null) => { + return setSessionState(sessionState); + }); + }; + + const tryRetrievingUserInfo = async (): Promise => { + if (await isSignedIn()) { + await startAutoRefreshToken(); + + // Enable OIDC Sessions Management only if it is set to true in the config. + if (config.syncSession) { + checkSession(); + } + + return getUser(); + } + + return Promise.resolve(undefined); + }; + + /** + * Initiates the authentication flow. + * + * @returns {Promise} A promise that resolves when authentication is successful. + */ + const signIn = async ( + params?: ExtendedAuthorizeRequestUrlParams, + authorizationCode?: string, + sessionState?: string, + state?: string, + tokenRequestConfig?: { + params: Record; + }, + ): Promise => { + const basicUserInfo = await _authenticationHelper.handleSignIn( + shouldStopAuthn, + checkSession, + tryRetrievingUserInfo, + ); + + if (basicUserInfo) { + return basicUserInfo; + } else { + let resolvedAuthorizationCode: string; + let resolvedSessionState: string; + let resolvedState: string; + + if (config?.responseMode === 'form_post' && authorizationCode) { + resolvedAuthorizationCode = authorizationCode; + resolvedSessionState = sessionState ?? ''; + resolvedState = state ?? ''; + } else { + resolvedAuthorizationCode = + new URL(window.location.href).searchParams.get(OIDCRequestConstants.Params.AUTHORIZATION_CODE) ?? ''; + resolvedSessionState = + new URL(window.location.href).searchParams.get(OIDCRequestConstants.Params.SESSION_STATE) ?? ''; + resolvedState = new URL(window.location.href).searchParams.get(OIDCRequestConstants.Params.STATE) ?? ''; + + SPAUtils.removeAuthorizationCode(); + } + + if (resolvedAuthorizationCode && resolvedState) { + return requestAccessToken(resolvedAuthorizationCode, resolvedSessionState, resolvedState, tokenRequestConfig); + } + + return getSignInUrl(params) + .then(async (response: AuthorizationResponse) => { + location.href = response.authorizationURL; + + await SPAUtils.waitTillPageRedirect(); + + return Promise.resolve({ + allowedScopes: '', + displayName: '', + email: '', + sessionState: '', + sub: '', + tenantDomain: '', + username: '', + }); + }) + .catch(error => { + return Promise.reject(error); + }); + } + }; + + /** + * Initiates the sign out flow. + * + * @returns {Promise} A promise that resolves when sign out is completed. + */ + const signOut = (): Promise => { + return new Promise((resolve, reject) => { + if (!_getSignOutURLFromSessionStorage) { + const message: Message = { + type: SIGN_OUT, + }; + + return communicate(message) + .then(async response => { + window.location.href = response; + + await SPAUtils.waitTillPageRedirect(); + + return resolve(true); + }) + .catch(error => { + return reject(error); + }); + } else { + window.location.href = SPAUtils.getSignOutUrl(config.clientId, instanceID); + + return SPAUtils.waitTillPageRedirect().then(() => { + return Promise.resolve(true); + }); + } + }); + }; + + /** + * Revokes token. + * + * @returns {Promise} A promise that resolves when revoking is completed. + */ + const revokeAccessToken = (): Promise => { + const message: Message = { + type: REVOKE_ACCESS_TOKEN, + }; + + return communicate(message) + .then(response => { + _sessionManagementHelper.reset(); + return Promise.resolve(response); + }) + .catch(error => { + return Promise.reject(error); + }); + }; + + const getOpenIDProviderEndpoints = (): Promise => { + const message: Message = { + type: GET_OIDC_SERVICE_ENDPOINTS, + }; + + return communicate(message) + .then(response => { + return Promise.resolve(response); + }) + .catch(error => { + return Promise.reject(error); + }); + }; + + const getConfigData = (): Promise> => { + const message: Message = { + type: GET_CONFIG_DATA, + }; + + return communicate>(message) + .then(response => { + return Promise.resolve(response); + }) + .catch(error => { + return Promise.reject(error); + }); + }; + + const getUser = (): Promise => { + const message: Message = { + type: GET_BASIC_USER_INFO, + }; + + return communicate(message) + .then(response => { + return Promise.resolve(response); + }) + .catch(error => { + return Promise.reject(error); + }); + }; + + const getDecodedIdToken = (sessionId?: string): Promise => { + const message: Message = { + type: GET_DECODED_ID_TOKEN, + }; + + return communicate(message) + .then(response => { + return Promise.resolve(response); + }) + .catch(error => { + return Promise.reject(error); + }); + }; + + const getDecodedIDPIDToken = (): Promise => { + const message: Message = { + type: GET_DECODED_IDP_ID_TOKEN, + }; + + return communicate(message) + .then(response => { + return Promise.resolve(response); + }) + .catch(error => { + return Promise.reject(error); + }); + }; + + const getCrypto = (): Promise => { + const message: Message = { + type: GET_CRYPTO_HELPER, + }; + + return communicate(message) + .then(response => { + return Promise.resolve(response); + }) + .catch(error => { + return Promise.reject(error); + }); + }; + + const getIdToken = (): Promise => { + const message: Message = { + type: GET_ID_TOKEN, + }; + + return communicate(message) + .then(response => { + return Promise.resolve(response); + }) + .catch(error => { + return Promise.reject(error); + }); + }; + + const isSignedIn = (): Promise => { + const message: Message = { + type: IS_AUTHENTICATED, + }; + + return communicate(message) + .then(response => { + return Promise.resolve(response); + }) + .catch(error => { + return Promise.reject(error); + }); + }; + + const refreshAccessToken = (): Promise => { + const message: Message = { + type: REFRESH_ACCESS_TOKEN, + }; + + return communicate(message); + }; + + const setHttpRequestSuccessCallback = (callback: (response: HttpResponse) => void): void => { + if (callback && typeof callback === 'function') { + httpClientHandlers.requestSuccessCallback = callback; + } + }; + + const setHttpRequestErrorCallback = (callback: (response: HttpError) => void | Promise): void => { + if (callback && typeof callback === 'function') { + httpClientHandlers.requestErrorCallback = callback; + } + }; + + const setHttpRequestStartCallback = (callback: () => void): void => { + if (callback && typeof callback === 'function') { + httpClientHandlers.requestStartCallback = callback; + } + }; + + const setHttpRequestFinishCallback = (callback: () => void): void => { + if (callback && typeof callback === 'function') { + httpClientHandlers.requestFinishCallback = callback; + } + }; + + const reInitialize = async (newConfig: Partial>): Promise => { + const existingConfig = await getConfigData(); + const isCheckSessionIframeDifferent: boolean = !( + existingConfig && + existingConfig.endpoints && + existingConfig.endpoints.checkSessionIframe && + newConfig && + newConfig.endpoints && + newConfig.endpoints.checkSessionIframe && + existingConfig.endpoints.checkSessionIframe === newConfig.endpoints.checkSessionIframe + ); + const config = {...existingConfig, ...newConfig}; + + const message: Message>> = { + data: config, + type: UPDATE_CONFIG, + }; + + await communicate>, void>(message); + + // Re-initiates check session if the check session endpoint is updated. + if (config.syncSession && isCheckSessionIframeDifferent) { + _sessionManagementHelper.reset(); + + checkSession(); + } + }; + + const decodeJwtToken = async >(token: string): Promise => { + return _authenticationClient.decodeJwtToken(token); + }; + + return { + disableHttpHandler, + enableHttpHandler, + getUser, + getConfigData, + getCrypto, + getDecodedIDPIDToken, + getDecodedIdToken, + getIdToken, + getOpenIDProviderEndpoints, + httpRequest, + httpRequestAll, + initialize, + isSignedIn, + refreshAccessToken, + exchangeToken, + revokeAccessToken, + setHttpRequestErrorCallback, + setHttpRequestFinishCallback, + setHttpRequestStartCallback, + setHttpRequestSuccessCallback, + signIn, + signOut, + signInSilently, + reInitialize, + decodeJwtToken + }; +}; diff --git a/packages/browser/src/__legacy__/worker/worker-core.ts b/packages/browser/src/__legacy__/worker/worker-core.ts index 9fcb9c6f5..6fc518a46 100755 --- a/packages/browser/src/__legacy__/worker/worker-core.ts +++ b/packages/browser/src/__legacy__/worker/worker-core.ts @@ -1,244 +1,244 @@ -/** - * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { - AsgardeoAuthClient, - AuthClientConfig, - AuthorizeRequestUrlParams, - User, - IsomorphicCrypto, - TokenExchangeRequestConfig, - IdToken, - OIDCEndpoints, - OIDCRequestConstants, - SessionData, - Storage, -} from '@asgardeo/javascript'; -import {AuthenticationHelper, SPAHelper} from '../helpers'; -import {HttpClient, HttpClientInstance} from '../http-client'; -import { - AuthorizationResponse, - HttpRequestConfig, - HttpResponse, - WebWorkerClientConfig, - WebWorkerCoreInterface, -} from '../models'; -import {MemoryStore} from '../stores'; -import {SPACryptoUtils} from '../utils/crypto-utils'; - -export const WebWorkerCore = async ( - instanceID: number, - config: AuthClientConfig, - getAuthHelper: ( - authClient: AsgardeoAuthClient, - spaHelper: SPAHelper, - ) => AuthenticationHelper, -): Promise => { - const _store: Storage = new MemoryStore(); - const _cryptoUtils: SPACryptoUtils = new SPACryptoUtils(); - const _authenticationClient = new AsgardeoAuthClient(); - await _authenticationClient.initialize(config, _store, _cryptoUtils, instanceID); - - const _spaHelper = new SPAHelper(_authenticationClient); - - const _authenticationHelper: AuthenticationHelper = getAuthHelper( - _authenticationClient, - _spaHelper, - ); - - const _dataLayer = _authenticationClient.getStorageManager(); - - const _httpClient: HttpClientInstance = HttpClient.getInstance(instanceID); - - const attachToken = async (request: HttpRequestConfig): Promise => { - await _authenticationHelper.attachTokenToRequestConfig(request); - }; - - _httpClient?.init && (await _httpClient.init(true, attachToken)); - - const setHttpRequestStartCallback = (callback: () => void): void => { - _httpClient?.setHttpRequestStartCallback && _httpClient.setHttpRequestStartCallback(callback); - }; - - const setHttpRequestSuccessCallback = (callback: (response: HttpResponse) => void): void => { - _httpClient?.setHttpRequestSuccessCallback && _httpClient.setHttpRequestSuccessCallback(callback); - }; - - const setHttpRequestFinishCallback = (callback: () => void): void => { - _httpClient?.setHttpRequestFinishCallback && _httpClient.setHttpRequestFinishCallback(callback); - }; - - const httpRequest = async (requestConfig: HttpRequestConfig): Promise => { - return await _authenticationHelper.httpRequest(_httpClient, requestConfig); - }; - - const httpRequestAll = async (requestConfigs: HttpRequestConfig[]): Promise => { - return await _authenticationHelper.httpRequestAll(requestConfigs, _httpClient); - }; - - const enableHttpHandler = (): void => { - _authenticationHelper.enableHttpHandler(_httpClient); - }; - - const disableHttpHandler = (): void => { - _authenticationHelper.disableHttpHandler(_httpClient); - }; - - const getSignInUrl = async (params?: AuthorizeRequestUrlParams): Promise => { - return _authenticationClient - .getSignInUrl(params) - .then(async (url: string) => { - const urlObject: URL = new URL(url); - const state: string = urlObject.searchParams.get(OIDCRequestConstants.Params.STATE) ?? ''; - const pkce: string = await _authenticationClient.getPKCECode(state); - - return {authorizationURL: url, pkce: pkce}; - }) - .catch(error => Promise.reject(error)); - }; - - const startAutoRefreshToken = async (): Promise => { - _spaHelper.clearRefreshTokenTimeout(); - _spaHelper.refreshAccessTokenAutomatically(_authenticationHelper); - - return; - }; - - const requestAccessToken = async ( - authorizationCode?: string, - sessionState?: string, - pkce?: string, - state?: string, - ): Promise => { - return await _authenticationHelper.requestAccessToken(authorizationCode, sessionState, undefined, pkce, state); - }; - - const signOut = async (): Promise => { - _spaHelper.clearRefreshTokenTimeout(); - - return await _authenticationClient.getSignOutUrl(); - }; - - const getSignOutUrl = async (): Promise => { - return await _authenticationClient.getSignOutUrl(); - }; - - const exchangeToken = async (config: TokenExchangeRequestConfig): Promise => { - return await _authenticationHelper.exchangeToken(config); - }; - - const refreshAccessToken = async (): Promise => { - try { - return await _authenticationHelper.refreshAccessToken(); - } catch (error) { - return Promise.reject(error); - } - }; - - const revokeAccessToken = async (): Promise => { - const timer: number = await _spaHelper.getRefreshTimeoutTimer(); - - return _authenticationClient - .revokeAccessToken() - .then(() => { - _spaHelper.clearRefreshTokenTimeout(timer); - - return Promise.resolve(true); - }) - .catch(error => Promise.reject(error)); - }; - - const getUser = async (): Promise => { - return _authenticationHelper.getUser(); - }; - - const getDecodedIdToken = async (sessionId?: string): Promise => { - return _authenticationHelper.getDecodedIdToken(sessionId); - }; - - const getCrypto = async (): Promise => { - return _authenticationHelper.getCrypto(); - }; - - const getDecodedIDPIDToken = async (): Promise => { - return _authenticationHelper.getDecodedIDPIDToken(); - }; - - const getIdToken = async (): Promise => { - return _authenticationHelper.getIdToken(); - }; - const getOpenIDProviderEndpoints = async (): Promise => { - return _authenticationHelper.getOpenIDProviderEndpoints(); - }; - - const getAccessToken = (): Promise => { - return _authenticationHelper.getAccessToken(); - }; - - const isSignedIn = (): Promise => { - return _authenticationHelper.isSignedIn(); - }; - - const setSessionState = async (sessionState: string): Promise => { - await _dataLayer.setSessionDataParameter( - OIDCRequestConstants.Params.SESSION_STATE as keyof SessionData, - sessionState, - ); - - return; - }; - - const reInitialize = async (config: Partial>): Promise => { - await _authenticationClient.reInitialize(config); - - return; - }; - - const getConfigData = async (): Promise> => { - return _dataLayer.getConfigData(); - }; - - return { - disableHttpHandler, - enableHttpHandler, - getAccessToken, - getSignInUrl, - getUser, - getConfigData, - getCrypto, - getDecodedIDPIDToken, - getDecodedIdToken, - getIdToken, - getOpenIDProviderEndpoints, - getSignOutUrl, - httpRequest, - httpRequestAll, - isSignedIn, - refreshAccessToken, - requestAccessToken, - exchangeToken, - revokeAccessToken, - setHttpRequestFinishCallback, - setHttpRequestStartCallback, - setHttpRequestSuccessCallback, - setSessionState, - signOut, - startAutoRefreshToken, - reInitialize, - }; -}; +/** + * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { + AsgardeoAuthClient, + AuthClientConfig, + AuthorizeRequestUrlParams, + User, + IsomorphicCrypto, + TokenExchangeRequestConfig, + IdToken, + OIDCEndpoints, + OIDCRequestConstants, + SessionData, + Storage, +} from '@asgardeo/javascript'; +import {AuthenticationHelper, SPAHelper} from '../helpers'; +import {HttpClient, HttpClientInstance} from '../http-client'; +import { + AuthorizationResponse, + HttpRequestConfig, + HttpResponse, + WebWorkerClientConfig, + WebWorkerCoreInterface, +} from '../models'; +import {MemoryStore} from '../stores'; +import {SPACryptoUtils} from '../utils/crypto-utils'; + +export const WebWorkerCore = async ( + instanceID: number, + config: AuthClientConfig, + getAuthHelper: ( + authClient: AsgardeoAuthClient, + spaHelper: SPAHelper, + ) => AuthenticationHelper, +): Promise => { + const _store: Storage = new MemoryStore(); + const _cryptoUtils: SPACryptoUtils = new SPACryptoUtils(); + const _authenticationClient = new AsgardeoAuthClient(); + await _authenticationClient.initialize(config, _store, _cryptoUtils, instanceID); + + const _spaHelper = new SPAHelper(_authenticationClient); + + const _authenticationHelper: AuthenticationHelper = getAuthHelper( + _authenticationClient, + _spaHelper, + ); + + const _dataLayer = _authenticationClient.getStorageManager(); + + const _httpClient: HttpClientInstance = HttpClient.getInstance(instanceID); + + const attachToken = async (request: HttpRequestConfig): Promise => { + await _authenticationHelper.attachTokenToRequestConfig(request); + }; + + _httpClient?.init && (await _httpClient.init(true, attachToken)); + + const setHttpRequestStartCallback = (callback: () => void): void => { + _httpClient?.setHttpRequestStartCallback && _httpClient.setHttpRequestStartCallback(callback); + }; + + const setHttpRequestSuccessCallback = (callback: (response: HttpResponse) => void): void => { + _httpClient?.setHttpRequestSuccessCallback && _httpClient.setHttpRequestSuccessCallback(callback); + }; + + const setHttpRequestFinishCallback = (callback: () => void): void => { + _httpClient?.setHttpRequestFinishCallback && _httpClient.setHttpRequestFinishCallback(callback); + }; + + const httpRequest = async (requestConfig: HttpRequestConfig): Promise => { + return await _authenticationHelper.httpRequest(_httpClient, requestConfig); + }; + + const httpRequestAll = async (requestConfigs: HttpRequestConfig[]): Promise => { + return await _authenticationHelper.httpRequestAll(requestConfigs, _httpClient); + }; + + const enableHttpHandler = (): void => { + _authenticationHelper.enableHttpHandler(_httpClient); + }; + + const disableHttpHandler = (): void => { + _authenticationHelper.disableHttpHandler(_httpClient); + }; + + const getSignInUrl = async (params?: AuthorizeRequestUrlParams): Promise => { + return _authenticationClient + .getSignInUrl(params) + .then(async (url: string) => { + const urlObject: URL = new URL(url); + const state: string = urlObject.searchParams.get(OIDCRequestConstants.Params.STATE) ?? ''; + const pkce: string = await _authenticationClient.getPKCECode(state); + + return {authorizationURL: url, pkce: pkce}; + }) + .catch(error => Promise.reject(error)); + }; + + const startAutoRefreshToken = async (): Promise => { + _spaHelper.clearRefreshTokenTimeout(); + _spaHelper.refreshAccessTokenAutomatically(_authenticationHelper); + + return; + }; + + const requestAccessToken = async ( + authorizationCode?: string, + sessionState?: string, + pkce?: string, + state?: string, + ): Promise => { + return await _authenticationHelper.requestAccessToken(authorizationCode, sessionState, undefined, pkce, state); + }; + + const signOut = async (): Promise => { + _spaHelper.clearRefreshTokenTimeout(); + + return await _authenticationClient.getSignOutUrl(); + }; + + const getSignOutUrl = async (): Promise => { + return await _authenticationClient.getSignOutUrl(); + }; + + const exchangeToken = async (config: TokenExchangeRequestConfig): Promise => { + return await _authenticationHelper.exchangeToken(config); + }; + + const refreshAccessToken = async (): Promise => { + try { + return await _authenticationHelper.refreshAccessToken(); + } catch (error) { + return Promise.reject(error); + } + }; + + const revokeAccessToken = async (): Promise => { + const timer: number = await _spaHelper.getRefreshTimeoutTimer(); + + return _authenticationClient + .revokeAccessToken() + .then(() => { + _spaHelper.clearRefreshTokenTimeout(timer); + + return Promise.resolve(true); + }) + .catch(error => Promise.reject(error)); + }; + + const getUser = async (): Promise => { + return _authenticationHelper.getUser(); + }; + + const getDecodedIdToken = async (sessionId?: string): Promise => { + return _authenticationHelper.getDecodedIdToken(sessionId); + }; + + const getCrypto = async (): Promise => { + return _authenticationHelper.getCrypto(); + }; + + const getDecodedIDPIDToken = async (): Promise => { + return _authenticationHelper.getDecodedIDPIDToken(); + }; + + const getIdToken = async (): Promise => { + return _authenticationHelper.getIdToken(); + }; + const getOpenIDProviderEndpoints = async (): Promise => { + return _authenticationHelper.getOpenIDProviderEndpoints(); + }; + + const getAccessToken = (): Promise => { + return _authenticationHelper.getAccessToken(); + }; + + const isSignedIn = (): Promise => { + return _authenticationHelper.isSignedIn(); + }; + + const setSessionState = async (sessionState: string): Promise => { + await _dataLayer.setSessionDataParameter( + OIDCRequestConstants.Params.SESSION_STATE as keyof SessionData, + sessionState, + ); + + return; + }; + + const reInitialize = async (config: Partial>): Promise => { + await _authenticationClient.reInitialize(config); + + return; + }; + + const getConfigData = async (): Promise> => { + return _dataLayer.getConfigData(); + }; + + return { + disableHttpHandler, + enableHttpHandler, + getAccessToken, + getSignInUrl, + getUser, + getConfigData, + getCrypto, + getDecodedIDPIDToken, + getDecodedIdToken, + getIdToken, + getOpenIDProviderEndpoints, + getSignOutUrl, + httpRequest, + httpRequestAll, + isSignedIn, + refreshAccessToken, + requestAccessToken, + exchangeToken, + revokeAccessToken, + setHttpRequestFinishCallback, + setHttpRequestStartCallback, + setHttpRequestSuccessCallback, + setSessionState, + signOut, + startAutoRefreshToken, + reInitialize, + }; +}; diff --git a/packages/browser/src/__legacy__/worker/worker-receiver.ts b/packages/browser/src/__legacy__/worker/worker-receiver.ts index 8631242ac..ac75e7e78 100644 --- a/packages/browser/src/__legacy__/worker/worker-receiver.ts +++ b/packages/browser/src/__legacy__/worker/worker-receiver.ts @@ -1,315 +1,315 @@ -/** - * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import {AsgardeoAuthClient, AsgardeoAuthException, AuthClientConfig, User} from '@asgardeo/javascript'; -import {WebWorkerCore} from './worker-core'; -import { - DISABLE_HTTP_HANDLER, - ENABLE_HTTP_HANDLER, - GET_AUTH_URL, - GET_BASIC_USER_INFO, - GET_CONFIG_DATA, - GET_CRYPTO_HELPER, - GET_DECODED_ID_TOKEN, - GET_ID_TOKEN, - GET_OIDC_SERVICE_ENDPOINTS, - GET_SIGN_OUT_URL, - HTTP_REQUEST, - HTTP_REQUEST_ALL, - INIT, - IS_AUTHENTICATED, - REFRESH_ACCESS_TOKEN, - REQUEST_ACCESS_TOKEN, - REQUEST_CUSTOM_GRANT, - REQUEST_FINISH, - REQUEST_START, - REQUEST_SUCCESS, - REVOKE_ACCESS_TOKEN, - SET_SESSION_STATE, - SIGN_OUT, - START_AUTO_REFRESH_TOKEN, - UPDATE_CONFIG, -} from '../constants'; -import {AuthenticationHelper, SPAHelper} from '../helpers'; -import { - AuthorizationResponse, - HttpResponse, - WebWorkerClass, - WebWorkerClientConfig, - WebWorkerCoreInterface, -} from '../models'; -import {MessageUtils} from '../utils'; - -export const workerReceiver = ( - getAuthHelper: ( - authClient: AsgardeoAuthClient, - spaHelper: SPAHelper, - ) => AuthenticationHelper, -) => { - const ctx: WebWorkerClass = self as any; - - let webWorker: WebWorkerCoreInterface; - - ctx.onmessage = async ({data, ports}) => { - const port = ports[0]; - if (data.type !== INIT && !webWorker) { - port.postMessage( - MessageUtils.generateFailureMessage( - new AsgardeoAuthException( - 'SPA-CLIENT_WORKER-ONMSG-NF01', - 'The web worker has not been initialized yet.', - 'The initialize method needs to be called before the specified operation can be carried out.', - ), - ), - ); - - return; - } - - switch (data.type) { - case INIT: - try { - const {instanceID = 0, ...configData} = data.data; - const config: AuthClientConfig = {...configData}; - webWorker = await WebWorkerCore(instanceID, config, getAuthHelper); - webWorker.setHttpRequestFinishCallback(onRequestFinishCallback); - webWorker.setHttpRequestStartCallback(onRequestStartCallback); - webWorker.setHttpRequestSuccessCallback(onRequestSuccessCallback); - port.postMessage(MessageUtils.generateSuccessMessage()); - } catch (error) { - port.postMessage(MessageUtils.generateFailureMessage(error)); - } - - break; - case GET_AUTH_URL: - webWorker - .getSignInUrl(data?.data) - .then((response: AuthorizationResponse) => { - port.postMessage(MessageUtils.generateSuccessMessage(response)); - }) - .catch(error => { - port.postMessage(MessageUtils.generateFailureMessage(error)); - }); - - break; - case REQUEST_ACCESS_TOKEN: - webWorker - .requestAccessToken(data?.data?.code, data?.data?.sessionState, data?.data?.pkce, data?.data?.state) - .then((response: User) => { - port.postMessage(MessageUtils.generateSuccessMessage(response)); - }) - .catch(error => { - port.postMessage(MessageUtils.generateFailureMessage(error)); - }); - - break; - case HTTP_REQUEST: { - const request = data.data; - const requestData = request?.data; - if (data.data?.data?.formData === true) { - const formData = new FormData(); - for (const key in requestData) { - if (key === 'formData') { - continue; - } - formData.append(key, requestData[key]); - } - request.data = formData; - } - webWorker - .httpRequest(request) - .then(response => { - port.postMessage(MessageUtils.generateSuccessMessage(response)); - }) - .catch(error => { - port.postMessage(MessageUtils.generateFailureMessage(error)); - }); - - break; - } - case HTTP_REQUEST_ALL: - webWorker - .httpRequestAll(data.data) - .then(response => { - port.postMessage(MessageUtils.generateSuccessMessage(response)); - }) - .catch(error => { - port.postMessage(MessageUtils.generateFailureMessage(error)); - }); - - break; - case SIGN_OUT: - try { - port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.signOut())); - } catch (error) { - port.postMessage(MessageUtils.generateFailureMessage(error)); - } - - break; - case REQUEST_CUSTOM_GRANT: - webWorker - .exchangeToken(data.data) - .then(response => { - port.postMessage(MessageUtils.generateSuccessMessage(response)); - }) - .catch(error => { - port.postMessage(MessageUtils.generateFailureMessage(error)); - }); - - break; - case REVOKE_ACCESS_TOKEN: - webWorker - .revokeAccessToken() - .then(response => { - port.postMessage(MessageUtils.generateSuccessMessage(response)); - }) - .catch(error => { - port.postMessage(MessageUtils.generateFailureMessage(error)); - }); - break; - case GET_OIDC_SERVICE_ENDPOINTS: - try { - port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.getOpenIDProviderEndpoints())); - } catch (error) { - port.postMessage(MessageUtils.generateFailureMessage(error)); - } - - break; - case GET_BASIC_USER_INFO: - try { - port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.getUser())); - } catch (error) { - port.postMessage(MessageUtils.generateFailureMessage(error)); - } - - break; - case GET_DECODED_ID_TOKEN: - try { - port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.getDecodedIdToken())); - } catch (error) { - port.postMessage(MessageUtils.generateFailureMessage(error)); - } - - break; - case GET_CRYPTO_HELPER: - try { - port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.getCrypto())); - } catch (error) { - port.postMessage(MessageUtils.generateFailureMessage(error)); - } - - break; - case GET_ID_TOKEN: - try { - port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.getIdToken())); - } catch (error) { - port.postMessage(MessageUtils.generateFailureMessage(error)); - } - - break; - case ENABLE_HTTP_HANDLER: - webWorker.enableHttpHandler(); - port.postMessage(MessageUtils.generateSuccessMessage()); - - break; - case DISABLE_HTTP_HANDLER: - webWorker.disableHttpHandler(); - port.postMessage(MessageUtils.generateSuccessMessage()); - - break; - case IS_AUTHENTICATED: - try { - port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.isSignedIn())); - } catch (error) { - port.postMessage(MessageUtils.generateFailureMessage(error)); - } - - break; - case GET_SIGN_OUT_URL: - try { - port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.getSignOutUrl())); - } catch (error) { - port.postMessage(MessageUtils.generateFailureMessage(error)); - } - - break; - case REFRESH_ACCESS_TOKEN: - try { - port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.refreshAccessToken())); - } catch (error) { - port.postMessage(MessageUtils.generateFailureMessage(error)); - } - - break; - case START_AUTO_REFRESH_TOKEN: - try { - port.postMessage(MessageUtils.generateSuccessMessage(webWorker.startAutoRefreshToken())); - } catch (error) { - port.postMessage(MessageUtils.generateFailureMessage(error)); - } - - break; - case SET_SESSION_STATE: - try { - port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.setSessionState(data?.data))); - } catch (error) { - port.postMessage(MessageUtils.generateFailureMessage(error)); - } - - break; - case UPDATE_CONFIG: - try { - port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.reInitialize(data?.data))); - } catch (error) { - port.postMessage(MessageUtils.generateFailureMessage(error)); - } - - break; - case GET_CONFIG_DATA: - try { - port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.getConfigData())); - } catch (error) { - port.postMessage(MessageUtils.generateFailureMessage(error)); - } - - break; - default: - port?.postMessage( - MessageUtils.generateFailureMessage( - new AsgardeoAuthException( - 'SPA-CLIENT_WORKER-ONMSG-IV02', - 'The message type is invalid.', - `The message type provided, ${data.type}, is invalid.`, - ), - ), - ); - } - }; - - const onRequestStartCallback = (): void => { - ctx.postMessage({type: REQUEST_START}); - }; - - const onRequestSuccessCallback = (response: HttpResponse): void => { - ctx.postMessage({data: JSON.stringify(response ?? ''), type: REQUEST_SUCCESS}); - }; - - const onRequestFinishCallback = (): void => { - ctx.postMessage({type: REQUEST_FINISH}); - }; -}; +/** + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import {AsgardeoAuthClient, AsgardeoAuthException, AuthClientConfig, User} from '@asgardeo/javascript'; +import {WebWorkerCore} from './worker-core'; +import { + DISABLE_HTTP_HANDLER, + ENABLE_HTTP_HANDLER, + GET_AUTH_URL, + GET_BASIC_USER_INFO, + GET_CONFIG_DATA, + GET_CRYPTO_HELPER, + GET_DECODED_ID_TOKEN, + GET_ID_TOKEN, + GET_OIDC_SERVICE_ENDPOINTS, + GET_SIGN_OUT_URL, + HTTP_REQUEST, + HTTP_REQUEST_ALL, + INIT, + IS_AUTHENTICATED, + REFRESH_ACCESS_TOKEN, + REQUEST_ACCESS_TOKEN, + REQUEST_CUSTOM_GRANT, + REQUEST_FINISH, + REQUEST_START, + REQUEST_SUCCESS, + REVOKE_ACCESS_TOKEN, + SET_SESSION_STATE, + SIGN_OUT, + START_AUTO_REFRESH_TOKEN, + UPDATE_CONFIG, +} from '../constants'; +import {AuthenticationHelper, SPAHelper} from '../helpers'; +import { + AuthorizationResponse, + HttpResponse, + WebWorkerClass, + WebWorkerClientConfig, + WebWorkerCoreInterface, +} from '../models'; +import {MessageUtils} from '../utils'; + +export const workerReceiver = ( + getAuthHelper: ( + authClient: AsgardeoAuthClient, + spaHelper: SPAHelper, + ) => AuthenticationHelper, +) => { + const ctx: WebWorkerClass = self as any; + + let webWorker: WebWorkerCoreInterface; + + ctx.onmessage = async ({data, ports}) => { + const port = ports[0]; + if (data.type !== INIT && !webWorker) { + port.postMessage( + MessageUtils.generateFailureMessage( + new AsgardeoAuthException( + 'SPA-CLIENT_WORKER-ONMSG-NF01', + 'The web worker has not been initialized yet.', + 'The initialize method needs to be called before the specified operation can be carried out.', + ), + ), + ); + + return; + } + + switch (data.type) { + case INIT: + try { + const {instanceID = 0, ...configData} = data.data; + const config: AuthClientConfig = {...configData}; + webWorker = await WebWorkerCore(instanceID, config, getAuthHelper); + webWorker.setHttpRequestFinishCallback(onRequestFinishCallback); + webWorker.setHttpRequestStartCallback(onRequestStartCallback); + webWorker.setHttpRequestSuccessCallback(onRequestSuccessCallback); + port.postMessage(MessageUtils.generateSuccessMessage()); + } catch (error) { + port.postMessage(MessageUtils.generateFailureMessage(error)); + } + + break; + case GET_AUTH_URL: + webWorker + .getSignInUrl(data?.data) + .then((response: AuthorizationResponse) => { + port.postMessage(MessageUtils.generateSuccessMessage(response)); + }) + .catch(error => { + port.postMessage(MessageUtils.generateFailureMessage(error)); + }); + + break; + case REQUEST_ACCESS_TOKEN: + webWorker + .requestAccessToken(data?.data?.code, data?.data?.sessionState, data?.data?.pkce, data?.data?.state) + .then((response: User) => { + port.postMessage(MessageUtils.generateSuccessMessage(response)); + }) + .catch(error => { + port.postMessage(MessageUtils.generateFailureMessage(error)); + }); + + break; + case HTTP_REQUEST: { + const request = data.data; + const requestData = request?.data; + if (data.data?.data?.formData === true) { + const formData = new FormData(); + for (const key in requestData) { + if (key === 'formData') { + continue; + } + formData.append(key, requestData[key]); + } + request.data = formData; + } + webWorker + .httpRequest(request) + .then(response => { + port.postMessage(MessageUtils.generateSuccessMessage(response)); + }) + .catch(error => { + port.postMessage(MessageUtils.generateFailureMessage(error)); + }); + + break; + } + case HTTP_REQUEST_ALL: + webWorker + .httpRequestAll(data.data) + .then(response => { + port.postMessage(MessageUtils.generateSuccessMessage(response)); + }) + .catch(error => { + port.postMessage(MessageUtils.generateFailureMessage(error)); + }); + + break; + case SIGN_OUT: + try { + port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.signOut())); + } catch (error) { + port.postMessage(MessageUtils.generateFailureMessage(error)); + } + + break; + case REQUEST_CUSTOM_GRANT: + webWorker + .exchangeToken(data.data) + .then(response => { + port.postMessage(MessageUtils.generateSuccessMessage(response)); + }) + .catch(error => { + port.postMessage(MessageUtils.generateFailureMessage(error)); + }); + + break; + case REVOKE_ACCESS_TOKEN: + webWorker + .revokeAccessToken() + .then(response => { + port.postMessage(MessageUtils.generateSuccessMessage(response)); + }) + .catch(error => { + port.postMessage(MessageUtils.generateFailureMessage(error)); + }); + break; + case GET_OIDC_SERVICE_ENDPOINTS: + try { + port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.getOpenIDProviderEndpoints())); + } catch (error) { + port.postMessage(MessageUtils.generateFailureMessage(error)); + } + + break; + case GET_BASIC_USER_INFO: + try { + port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.getUser())); + } catch (error) { + port.postMessage(MessageUtils.generateFailureMessage(error)); + } + + break; + case GET_DECODED_ID_TOKEN: + try { + port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.getDecodedIdToken())); + } catch (error) { + port.postMessage(MessageUtils.generateFailureMessage(error)); + } + + break; + case GET_CRYPTO_HELPER: + try { + port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.getCrypto())); + } catch (error) { + port.postMessage(MessageUtils.generateFailureMessage(error)); + } + + break; + case GET_ID_TOKEN: + try { + port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.getIdToken())); + } catch (error) { + port.postMessage(MessageUtils.generateFailureMessage(error)); + } + + break; + case ENABLE_HTTP_HANDLER: + webWorker.enableHttpHandler(); + port.postMessage(MessageUtils.generateSuccessMessage()); + + break; + case DISABLE_HTTP_HANDLER: + webWorker.disableHttpHandler(); + port.postMessage(MessageUtils.generateSuccessMessage()); + + break; + case IS_AUTHENTICATED: + try { + port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.isSignedIn())); + } catch (error) { + port.postMessage(MessageUtils.generateFailureMessage(error)); + } + + break; + case GET_SIGN_OUT_URL: + try { + port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.getSignOutUrl())); + } catch (error) { + port.postMessage(MessageUtils.generateFailureMessage(error)); + } + + break; + case REFRESH_ACCESS_TOKEN: + try { + port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.refreshAccessToken())); + } catch (error) { + port.postMessage(MessageUtils.generateFailureMessage(error)); + } + + break; + case START_AUTO_REFRESH_TOKEN: + try { + port.postMessage(MessageUtils.generateSuccessMessage(webWorker.startAutoRefreshToken())); + } catch (error) { + port.postMessage(MessageUtils.generateFailureMessage(error)); + } + + break; + case SET_SESSION_STATE: + try { + port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.setSessionState(data?.data))); + } catch (error) { + port.postMessage(MessageUtils.generateFailureMessage(error)); + } + + break; + case UPDATE_CONFIG: + try { + port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.reInitialize(data?.data))); + } catch (error) { + port.postMessage(MessageUtils.generateFailureMessage(error)); + } + + break; + case GET_CONFIG_DATA: + try { + port.postMessage(MessageUtils.generateSuccessMessage(await webWorker.getConfigData())); + } catch (error) { + port.postMessage(MessageUtils.generateFailureMessage(error)); + } + + break; + default: + port?.postMessage( + MessageUtils.generateFailureMessage( + new AsgardeoAuthException( + 'SPA-CLIENT_WORKER-ONMSG-IV02', + 'The message type is invalid.', + `The message type provided, ${data.type}, is invalid.`, + ), + ), + ); + } + }; + + const onRequestStartCallback = (): void => { + ctx.postMessage({type: REQUEST_START}); + }; + + const onRequestSuccessCallback = (response: HttpResponse): void => { + ctx.postMessage({data: JSON.stringify(response ?? ''), type: REQUEST_SUCCESS}); + }; + + const onRequestFinishCallback = (): void => { + ctx.postMessage({type: REQUEST_FINISH}); + }; +};