Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/shy-suns-sleep.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@asgardeo/javascript': patch
'@asgardeo/browser': patch
'@asgardeo/react': patch
---

fix web-worker related issues
20 changes: 17 additions & 3 deletions packages/browser/esbuild.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import { readFileSync } from 'fs';
import * as esbuild from 'esbuild';
import { createRequire } from 'module';
import { fileURLToPath } from 'url';
import inlineWorkerPlugin from 'esbuild-plugin-inline-worker';

const require = createRequire(import.meta.url);
const pkg = JSON.parse(readFileSync('./package.json', 'utf8'));
Expand All @@ -36,7 +36,7 @@ const polyfillPlugin = {
build.onResolve({ filter: /^crypto$/ }, () => ({
path: require.resolve('crypto-browserify')
}));

// Buffer polyfill
build.onResolve({ filter: /^buffer$/ }, () => ({
path: require.resolve('buffer/')
Expand Down Expand Up @@ -71,7 +71,21 @@ const commonOptions = {
}
`
},
plugins: [polyfillPlugin]
plugins: [
polyfillPlugin,
inlineWorkerPlugin({
format: 'iife',
target: 'es2020',
platform: 'browser',
define: {
'global': 'self',
'globalThis': 'self',
'process.env.NODE_DEBUG': 'false',
'process.version': '"16.0.0"',
'process.browser': 'true'
}
})
]
};

await esbuild.build({
Expand Down
1 change: 1 addition & 0 deletions packages/browser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"@wso2/eslint-plugin": "catalog:",
"@wso2/prettier-config": "catalog:",
"esbuild": "^0.25.9",
"esbuild-plugin-inline-worker": "^0.1.1",
"esbuild-plugins-node-modules-polyfill": "^1.7.0",
"eslint": "8.57.0",
"playwright": "^1.52.0",
Expand Down
21 changes: 10 additions & 11 deletions packages/browser/src/__legacy__/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
OIDCEndpoints,
User,
} from '@asgardeo/javascript';
// import WorkerFile from '../worker';
import WorkerFile from '../web.worker';
import {MainThreadClient, WebWorkerClient} from './clients';
import {Hooks, REFRESH_ACCESS_TOKEN_ERR0R} from './constants';
import {AuthenticationHelper, SPAHelper} from './helpers';
Expand Down Expand Up @@ -70,8 +70,7 @@ export class AsgardeoSPAClient {
protected _client: WebWorkerClientInterface | MainThreadClientInterface | undefined;
protected _storage: BrowserStorage | undefined;
protected _authHelper: typeof AuthenticationHelper = AuthenticationHelper;
// protected _worker: new () => Worker = WorkerFile;
protected _worker = null;
protected _worker: new () => Worker = WorkerFile;
protected _initialized: boolean = false;
protected _startedInitialize: boolean = false;
protected _onSignInCallback: (response: User) => void = () => null;
Expand All @@ -94,13 +93,13 @@ export class AsgardeoSPAClient {
}
}

// public instantiateWorker(worker: new () => Worker) {
// if (worker) {
// this._worker = worker;
// } else {
// this._worker = WorkerFile;
// }
// }
public instantiateWorker(worker: new () => Worker) {
if (worker) {
this._worker = worker;
} else {
this._worker = WorkerFile;
}
}

/**
* This method specifies if the `AsgardeoSPAClient` has been initialized or not.
Expand Down Expand Up @@ -239,7 +238,7 @@ export class AsgardeoSPAClient {
this._startedInitialize = true;

authHelper && this.instantiateAuthHelper(authHelper);
// workerFile && this.instantiateWorker(workerFile);
workerFile && this.instantiateWorker(workerFile);

const _config = await this._client?.getConfigData();

Expand Down
31 changes: 26 additions & 5 deletions packages/browser/src/__legacy__/clients/web-worker-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,26 @@ export const WebWorkerClient = async (

resolve(responseData);
} else {
reject(data.error ? JSON.parse(data.error) : null);
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);
}
});
});
Expand Down Expand Up @@ -456,8 +475,8 @@ export const WebWorkerClient = async (
type: GET_AUTH_URL,
};

return communicate<ExtendedAuthorizeRequestUrlParams, AuthorizationResponse>(message).then(
async (response: AuthorizationResponse) => {
return communicate<ExtendedAuthorizeRequestUrlParams, AuthorizationResponse>(message)
.then(async (response: AuthorizationResponse) => {
if (response.pkce && config.enablePKCE) {
const pkceKey: string = extractPkceStorageKeyFromState(
new URL(response.authorizationURL).searchParams.get(OIDCRequestConstants.Params.STATE) ?? '',
Expand All @@ -467,8 +486,10 @@ export const WebWorkerClient = async (
}

return Promise.resolve(response);
},
);
})
.catch(error => {
return Promise.reject(error);
});
};

const requestAccessToken = async (
Expand Down
36 changes: 35 additions & 1 deletion packages/browser/src/__legacy__/utils/message-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,42 @@ export class MessageUtils {
delete error.toJSON;
}

let serializedError: string;

try {
// Handle Error objects specially
if (error instanceof Error) {
serializedError = JSON.stringify({
name: error.name,
message: error.message,
stack: error.stack,
// Copy any additional enumerable properties
...error
});
} else if (typeof error === 'object' && error !== null) {
// For other objects, try to stringify and fallback to a safe representation
try {
serializedError = JSON.stringify(error);
} catch {
serializedError = JSON.stringify({
message: error.toString ? error.toString() : 'Unknown error',
originalError: String(error)
});
}
} else {
// For primitives, stringify directly
serializedError = JSON.stringify(error ?? "Unknown error");
}
} catch {
// Final fallback if all else fails
serializedError = JSON.stringify({
message: 'Error serialization failed',
originalError: String(error)
});
}

return {
error: JSON.stringify(error ?? ""),
error: serializedError,
success: false
};
}
Expand Down
2 changes: 1 addition & 1 deletion packages/browser/src/models/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@

import {Config} from '@asgardeo/javascript';

export type AsgardeoBrowserConfig = Config;
export type AsgardeoBrowserConfig = Config<'sessionStorage' | 'localStorage' | 'browserMemory' | 'webWorker'>;
54 changes: 54 additions & 0 deletions packages/browser/src/types/worker.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com).
*
* WSO2 LLC. 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.
*/

// Type declarations for worker files handled by esbuild-plugin-inline-worker

declare module "*.worker" {
const WorkerFactory: {
new (): Worker;
};
export default WorkerFactory;
}

declare module "*.worker.js" {
const WorkerFactory: {
new (): Worker;
};
export default WorkerFactory;
}

declare module "*.worker.ts" {
const WorkerFactory: {
new (): Worker;
};
export default WorkerFactory;
}

declare module "*.worker.jsx" {
const WorkerFactory: {
new (): Worker;
};
export default WorkerFactory;
}

declare module "*.worker.tsx" {
const WorkerFactory: {
new (): Worker;
};
export default WorkerFactory;
}
22 changes: 0 additions & 22 deletions packages/browser/src/web-worker.d.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,25 @@
* under the License.
*/

import { Buffer } from 'buffer/';
import {AsgardeoAuthClient} from '@asgardeo/javascript';
import {AuthenticationHelper, SPAHelper} from './__legacy__/helpers';
import {WebWorkerClientConfig} from './__legacy__/models';
import {workerReceiver} from './__legacy__/worker/worker-receiver';

// Set up global polyfills
if (typeof self !== 'undefined' && !(self as any).Buffer) {
(self as any).Buffer = Buffer;
}

if (typeof self !== 'undefined') {
if (!(self as any).global) {
(self as any).global = self;
}
// Note: globalThis is read-only, so we don't try to override it
// The esbuild config already maps globalThis to self via define
}

workerReceiver((authClient: AsgardeoAuthClient<WebWorkerClientConfig>, spaHelper: SPAHelper<WebWorkerClientConfig>) => {
return new AuthenticationHelper(authClient, spaHelper);
});
Expand Down
6 changes: 6 additions & 0 deletions packages/javascript/src/models/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,12 @@ export interface BaseConfig<T = unknown> extends WithPreferences {
* @see {@link https://openid.net/specs/openid-connect-session-management-1_0.html#IframeBasedSessionManagement}
*/
syncSession?: boolean;

/**
* Storage mechanism to use for storing tokens and session data.
* The values should be defined at the framework layer.
*/
storage?: T;
}

export interface WithPreferences {
Expand Down
12 changes: 7 additions & 5 deletions packages/react/src/contexts/Asgardeo/AsgardeoContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
TokenResponse,
} from '@asgardeo/browser';
import AsgardeoReactClient from '../../AsgardeoReactClient';
import {AsgardeoReactConfig} from '../../models/config';

/**
* Props interface of {@link AsgardeoContext}
Expand Down Expand Up @@ -96,31 +97,31 @@ export type AsgardeoContextProps = {
* @example
* signInOptions: { prompt: "login", fidp: "OrganizationSSO" }
*/
signInOptions?: SignInOptions;
signInOptions: SignInOptions;
/**
* Function to retrieve the decoded ID token.
* This function decodes the ID token and returns its payload.
* It can be used to access user claims and other information contained in the ID token.
*
* @returns A promise that resolves to the decoded ID token payload.
*/
getDecodedIdToken?: () => Promise<IdToken>;
getDecodedIdToken: () => Promise<IdToken>;

/**
* Retrieves the access token stored in the storage.
* This function retrieves the access token and returns it.
* @remarks This does not work in the `webWorker` or any other worker environment.
* @returns A promise that resolves to the access token.
*/
getAccessToken?: () => Promise<string>;
getAccessToken: () => Promise<string>;

/**
* Swaps the current access token with a new one based on the provided configuration (with a grant type).
* @param config - Configuration for the token exchange request.
* @returns A promise that resolves to the token response or the raw response.
*/
exchangeToken?: (config: TokenExchangeRequestConfig) => Promise<TokenResponse | Response>;
};
exchangeToken: (config: TokenExchangeRequestConfig) => Promise<TokenResponse | Response>;
} & Pick<AsgardeoReactConfig, 'storage'>;

/**
* Context object for managing the Authentication flow builder core context.
Expand Down Expand Up @@ -149,6 +150,7 @@ const AsgardeoContext: Context<AsgardeoContextProps | null> = createContext<null
getDecodedIdToken: null,
getAccessToken: null,
exchangeToken: null,
storage: 'sessionStorage',
});

AsgardeoContext.displayName = 'AsgardeoContext';
Expand Down
Loading
Loading