Skip to content

Commit

Permalink
Merge pull request #515 from lifeomic/handle-concurrent-401s
Browse files Browse the repository at this point in the history
Handle Concurrent API Auth Failures
  • Loading branch information
sternetj committed Jan 15, 2024
2 parents fb97b6b + b39d330 commit f6130bb
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 15 deletions.
1 change: 1 addition & 0 deletions src/hooks/useAuth.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@ test('refreshForAuthFailure refreshes auth token if not already loading', async
jest.useFakeTimers();
act(() => {
result.current.refreshForAuthFailure(new Error() as AxiosError);
result.current.refreshForAuthFailure(new Error() as AxiosError);
});
expect(refreshHandler).toHaveBeenCalledTimes(1);
expect(result.current.loading).toBe(true);
Expand Down
46 changes: 31 additions & 15 deletions src/hooks/useAuth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React, {
useCallback,
useContext,
useEffect,
useRef,
useState,
} from 'react';
import { RefreshResult } from 'react-native-app-auth';
Expand Down Expand Up @@ -70,6 +71,7 @@ export const AuthContextProvider = ({
RefreshHandler | undefined
>();
const { currentAppState } = useCurrentAppState();
const refreshLock = useRef<Promise<void>>();

const storeAuthResult = useCallback(async (result: AuthResult) => {
await secureStorage.setObject(result);
Expand All @@ -87,23 +89,37 @@ export const AuthContextProvider = ({

const refreshAuthResult = useCallback(
async (_refreshHandler: RefreshHandler, _authResult: AuthResult) => {
if (__DEV__ && process.env.NODE_ENV !== 'test') {
console.warn('Attempting access token refresh');
const refreshSession = async () => {
if (__DEV__ && process.env.NODE_ENV !== 'test') {
console.log('Attempting access token refresh');
}
try {
setLoading(true);
const refreshResult = await _refreshHandler(_authResult);
await storeAuthResult({
...refreshResult,

// Careful not to lose `refreshToken`, which may not be in `refreshResult`
refreshToken:
refreshResult.refreshToken || _authResult.refreshToken,
});
} catch (error) {
if (process.env.NODE_ENV !== 'test') {
console.warn('Error occurred refreshing access token', error);
}
clearAuthResult();
}
};

if (refreshLock.current) {
return refreshLock.current;
}

try {
setLoading(true);
const refreshResult = await _refreshHandler(_authResult);
await storeAuthResult({
...refreshResult,

// Careful not to lose `refreshToken`, which may not be in `refreshResult`
refreshToken: refreshResult.refreshToken || _authResult.refreshToken,
});
} catch (error) {
if (process.env.NODE_ENV !== 'test') {
console.warn('Error occurred refreshing access token', error);
}
clearAuthResult();
refreshLock.current = refreshSession();
await refreshLock.current;
} finally {
refreshLock.current = undefined;
}
},
[storeAuthResult, clearAuthResult],
Expand Down

0 comments on commit f6130bb

Please sign in to comment.