From 8abc096232335bcd665fa84f3c4a91fbc30cabc5 Mon Sep 17 00:00:00 2001 From: Subhankar Maiti Date: Fri, 12 Sep 2025 01:12:07 +0530 Subject: [PATCH] fix concurrent credential storage errors by preventing unnecessary state updates --- src/core/utils/deepEqual.ts | 21 +++++++++++++++++++++ src/hooks/Auth0Provider.tsx | 24 +++++++++++++++++------- src/hooks/reducer.ts | 9 ++++++++- 3 files changed, 46 insertions(+), 8 deletions(-) create mode 100644 src/core/utils/deepEqual.ts diff --git a/src/core/utils/deepEqual.ts b/src/core/utils/deepEqual.ts new file mode 100644 index 00000000..eceaafba --- /dev/null +++ b/src/core/utils/deepEqual.ts @@ -0,0 +1,21 @@ +export function deepEqual(x: T, y: T): boolean { + if (x === y) { + return true; + } else if ( + typeof x == 'object' && + x != null && + typeof y == 'object' && + y != null + ) { + if (Object.keys(x).length != Object.keys(y).length) return false; + + for (const prop in x) { + if (Object.prototype.hasOwnProperty.call(y, prop)) { + if (!deepEqual(x[prop], y[prop])) return false; + } else return false; + } + return true; + } else { + return false; + } +} diff --git a/src/hooks/Auth0Provider.tsx b/src/hooks/Auth0Provider.tsx index 1f9755ab..0b0f7862 100644 --- a/src/hooks/Auth0Provider.tsx +++ b/src/hooks/Auth0Provider.tsx @@ -146,21 +146,31 @@ export const Auth0Provider = ({ ); const getCredentials = useCallback( - ( + async ( scope?: string, minTtl?: number, parameters?: Record, forceRefresh?: boolean - ) => - loginFlow( - client.credentialsManager.getCredentials( + ) => { + try { + const credentials = await client.credentialsManager.getCredentials( scope, minTtl, parameters, forceRefresh - ) - ), - [client, loginFlow] + ); + if (credentials.idToken) { + const user = Auth0User.fromIdToken(credentials.idToken); + dispatch({ type: 'SET_USER', user }); + } + return credentials; + } catch (e) { + const error = e as AuthError; + dispatch({ type: 'ERROR', error }); + throw error; + } + }, + [client] ); const hasValidCredentials = useCallback( diff --git a/src/hooks/reducer.ts b/src/hooks/reducer.ts index a657024d..3a745674 100644 --- a/src/hooks/reducer.ts +++ b/src/hooks/reducer.ts @@ -1,5 +1,6 @@ import type { User } from '../types'; import type { AuthError } from '../core/models'; +import { deepEqual } from '../core/utils/deepEqual'; /** * The shape of the authentication state managed by the Auth0Provider. @@ -18,7 +19,8 @@ export type AuthAction = | { type: 'LOGIN_COMPLETE'; user: User } | { type: 'LOGOUT_COMPLETE' } | { type: 'ERROR'; error: AuthError } - | { type: 'INITIALIZED'; user: User | null }; + | { type: 'INITIALIZED'; user: User | null } + | { type: 'SET_USER'; user: User | null }; /** * A pure function that calculates the new state based on the previous state and a dispatched action. @@ -34,5 +36,10 @@ export const reducer = (state: AuthState, action: AuthAction): AuthState => { return { ...state, isLoading: false, error: action.error }; case 'INITIALIZED': return { ...state, isLoading: false, user: action.user }; + case 'SET_USER': + if (deepEqual(state.user, action.user)) { + return state; + } + return { ...state, user: action.user }; } };