Fix failure of calling authenticated APIs when multiple AsgardeoProvider instances are used#334
Conversation
09e5dd9 to
db06041
Compare
…agnization support
db06041 to
2af8495
Compare
🦋 Changeset detectedThe changes in this PR will be included in the next version bump. Not sure what this means? Click here to learn what changesets are. |
DonOmalVindula
left a comment
There was a problem hiding this comment.
Reviewed the changes. Left some comments. Please address them before merging.
|
Issue: This file still has module-level request: AsgardeoSPAClient.getInstance().httpRequest.bind(AsgardeoSPAClient.getInstance()),
requestAll: AsgardeoSPAClient.getInstance().httpRequestAll.bind(AsgardeoSPAClient.getInstance()),This is the same pattern you fixed in all the const createHttp = (instanceId: number = 0) => {
const client = AsgardeoSPAClient.getInstance(instanceId);
return {
request: client.httpRequest.bind(client),
requestAll: client.httpRequestAll.bind(client),
};
};
export default createHttp;Or alternatively, if this is only used internally, pass the |
| @@ -250,6 +250,8 @@ export class AsgardeoAuthClient<T> { | |||
| authRequestConfig['client_secret'] = configData.clientSecret; | |||
| } | |||
|
|
|||
There was a problem hiding this comment.
This line is dead code. getAuthorizeRequestUrlParams already receives instanceId: this.getInstanceId().toString() below and handles the state parameter internally. Also, getAuthorizeRequestUrlParams explicitly filters out state from custom params:
if (key !== '' && value !== '' && key !== OIDCRequestConstants.Params.STATE) {So this value is never used. Remove this line:
| } | ||
|
|
||
| if (instanceID) { | ||
| if (instanceID !== undefined) { |
There was a problem hiding this comment.
Good catch fixing instanceID !== undefined here. But the block above (lines 124-128) still has the same falsy-zero problem:
if (!this.instanceIdValue) {
this.instanceIdValue = 0;
} else {
this.instanceIdValue += 1;
}If this.instanceIdValue is already 0, !this.instanceIdValue is true, so it resets to 0 instead of incrementing. Suggested fix — align with the cleaner logic in the browser package:
if (this.instanceIdValue === undefined || this.instanceIdValue === null) {
this.instanceIdValue = 0;
} else {
this.instanceIdValue += 1;
}
if (instanceID !== undefined) {
this.instanceIdValue = instanceID;
}| const getScim2Me = async ({fetcher, instanceId = 0, ...requestConfig}: GetScim2MeConfig): Promise<User> => { | ||
| const defaultFetcher = async (url: string, config: RequestInit): Promise<Response> => { | ||
| const httpClient: HttpInstance = AsgardeoSPAClient.getInstance(instanceId).httpRequest.bind( |
There was a problem hiding this comment.
getInstance is called twice. Store it once:
| const getScim2Me = async ({fetcher, instanceId = 0, ...requestConfig}: GetScim2MeConfig): Promise<User> => { | |
| const defaultFetcher = async (url: string, config: RequestInit): Promise<Response> => { | |
| const httpClient: HttpInstance = AsgardeoSPAClient.getInstance(instanceId).httpRequest.bind( | |
| const client = AsgardeoSPAClient.getInstance(instanceId); | |
| const httpClient: HttpInstance = client.httpRequest.bind(client); |
Check in other API files as well.
| --- | ||
| '@asgardeo/javascript': patch | ||
| '@asgardeo/browser': patch | ||
| '@asgardeo/react': patch |
There was a problem hiding this comment.
The return type of getInstance() changed from AsgardeoSPAClient | undefined to AsgardeoSPAClient, and new public methods were added (hasInstance, destroyInstance, getInstanceKeys, destroyAllInstances, getInstanceId). This is a public API surface change — should be a minor bump instead of patch:
| '@asgardeo/react': patch | |
| '@asgardeo/javascript': minor | |
| '@asgardeo/browser': minor | |
| '@asgardeo/react': patch |
| /** | ||
| * This method returns all active instance IDs. | ||
| * Useful for debugging or managing multiple instances. | ||
| * |
There was a problem hiding this comment.
@preserve is only needed on methods that are part of the public API and should survive minification. Debugging/testing utilities like getInstanceKeys() and destroyAllInstances() don't need it. Remove @preserve from these utility methods. For example:
/**
* This method returns all active instance IDs.
*
* @return {number[]} - Returns an array of all active instance IDs.
*
* @memberof AsgardeoSPAClient
*/
public static getInstanceKeys(): number[] {
return Array.from(this._instances.keys());
}Apply the same for destroyAllInstances and hasInstance.
| * | ||
| * // Remove the default instance | ||
| * AsgardeoSPAClient.destroyInstance(); | ||
| * ``` |
There was a problem hiding this comment.
destroyInstance only removes the instance from the map. It doesn't clean up the underlying auth state (storage, tokens, session data, etc.) that the instance manages. If someone calls destroyInstance(1) and then getInstance(1), they'll get a fresh instance but the old storage data for instance_1 is still sitting in session/local storage. Suggested fix:
public static destroyInstance(id: number = 0): boolean {
const instance = this._instances.get(id);
if (instance) {
// Clear session/storage data for this instance before removing
instance.clearSession?.();
}
return this._instances.delete(id);
}Same applies for destroyAllInstances — iterate and clean up each instance before clearing the map.
This pull request introduces adoption of a Multiton pattern in AsgardeoSPAClient, allowing multiple authentication contexts to coexist within the same application by managing client instances via unique IDs. Several React level APIs have been updated to accept and propagate these instance IDs, ensuring correct isolation and management of authentication state per instance.
Multiton pattern implementation and API updates for instance-awareness::
AsgardeoSPAClientinclient.tsto implement a Multiton pattern, including new static methods for managing instances (getInstance,hasInstance,destroyInstance,getInstanceKeys,destroyAllInstances), and added an instance ID accessor (getInstanceId). [1] [2] [3]AsgardeoReactClientand all organization-related API functions (createOrganization,getAllOrganizations,getMeOrganizations,getOrganization) in React to accept an optionalinstanceIdparameter and use the correct client instance for requests. [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11]Related Issues
Related PRs
Checklist
Security checks