🚨 CRITICAL Bug Report
Severity: HIGH - App gets stuck in authenticated state, preventing route guards from working
Description
When the refresh token expires, isAuthenticated never changes to false, leaving the app stuck in an authenticated state. This breaks protected routes and auth guards. Additionally, users encounter a JSON parse error from getUserInfo.
User Impact
- Route guards don't trigger - Users remain on protected screens even though they're logged out
- Expo Router protected routes fail - Navigation doesn't switch to login screen
- Apps get stuck in an inconsistent auth state
- JSON parse errors appear in logs
Error Message
ERROR [OIDC-AUTH PROVIDER] get user info failed [SyntaxError: JSON Parse error: Unexpected end of input]
SyntaxError: JSON Parse error: Unexpected end of input
Root Cause
Two critical issues in the provider's state change handler:
Issue 1: Unhandled Exception Blocks State Update
In packages/react-native-oidc-auth-core/src/provider/oidc-auth/oidc-auth.tsx (lines 46-56):
const subscription = instance.onStateChanged(state => {
tracingLog.info(`[OIDC-AUTH PROVIDER] state changed: ${state}`);
if (state === EventState.TOKEN_EXPIRED) {
instance.updateToken(); // ❌ NOT AWAITED, NO ERROR HANDLING!
} else if (state === EventState.INIT_COMPLETED) {
setIsInitializing(true);
}
setIsAuthenticated(instance.isAuthenticated()); // ❌ NEVER REACHED IF updateToken() THROWS!
});
What happens:
TOKEN_EXPIRED event fires
instance.updateToken() is called
- If refresh token is null/expired,
updateToken() throws an error (line 427 in oidc-auth.ts)
- Exception bubbles up and prevents
setIsAuthenticated() from being called
- App stays stuck with
isAuthenticated = true ❌
Issue 2: Missing REFRESH_ERROR Event Handler
When updateToken() doesn't throw but the refresh fails:
clearToken() is called, setting authenticated = false internally
REFRESH_ERROR event is emitted (line 487 in oidc-auth.ts)
- Provider doesn't handle
REFRESH_ERROR event - only handles TOKEN_EXPIRED and INIT_COMPLETED
- State is never updated in the provider
- App stays stuck with
isAuthenticated = true ❌
Code Location
File: packages/react-native-oidc-auth-core/src/provider/oidc-auth/oidc-auth.tsx
Problem 1 (lines 46-56): No error handling for updateToken()
Problem 2 (lines 46-56): Missing handler for REFRESH_ERROR event
Expected Behavior
- When refresh token expires,
isAuthenticated should reliably change to false
- Route guards should trigger and redirect to login
- No unhandled errors should crash the state update
Actual Behavior
isAuthenticated never changes when refresh token expires
- App stuck in authenticated state
- Protected routes remain accessible
- JSON parse errors appear
- Route navigation doesn't trigger
Proposed Solution
Fix both issues with proper error handling and event listening:
useEffect(() => {
const subscription = instance.onStateChanged(state => {
tracingLog.info(`[OIDC-AUTH PROVIDER] state changed: ${state}`);
if (state === EventState.TOKEN_EXPIRED) {
// Properly handle updateToken errors
instance.updateToken().catch(e => {
tracingLog.error('[OIDC-AUTH PROVIDER] updateToken failed', e);
// Force state update even if updateToken throws
setIsAuthenticated(instance.isAuthenticated());
});
} else if (state === EventState.INIT_COMPLETED) {
setIsInitializing(true);
} else if (state === EventState.REFRESH_ERROR) {
// Handle refresh error event - state update is critical here
tracingLog.info('[OIDC-AUTH PROVIDER] refresh failed, updating auth state');
}
// Always update authentication state
setIsAuthenticated(instance.isAuthenticated());
});
tracingLog.debug('[OIDC-AUTH PROVIDER] Initialization provider');
instance
.initState()
.catch(e =>
tracingLog.error('[OIDC-AUTH PROVIDER] initialization failed', e),
);
return subscription;
}, []);
Additional improvement - Clean up user state:
useEffect(() => {
const getUserInfo = async () => {
let userInfo: User | null = null;
try {
userInfo = await instance.getUserInfo();
setUserFetchSuccess(true);
setUserFetchError(false);
} catch (e) {
setUserFetchError(true);
setUserFetchSuccess(false);
tracingLog.error('[OIDC-AUTH PROVIDER] get user info failed', e);
}
setUser(userInfo);
};
if (isAuthenticated) {
getUserInfo();
} else {
// Clean up user state when logged out
setUser(null);
setUserFetchSuccess(false);
setUserFetchError(false);
}
}, [isAuthenticated, instance]);
Optionally - Better error messages in getUserInfo implementations:
In both oidc-auth-bare.ts and oidc-auth-expo.ts, wrap the getUserInfo calls in try-catch to provide clearer error messages about token expiration.
Impact
- Severity: CRITICAL - Completely breaks authentication flow
- Frequency: Occurs every time a refresh token expires
- User Experience: App becomes unusable - users can't be logged out, route guards fail
- Security: Users may access protected content when they shouldn't
Reproduction Steps
- Log in to app with OIDC auth
- Wait for refresh token to expire (or manually delete it from storage)
- Trigger token refresh (e.g., make an API call)
- Observe:
isAuthenticated remains true, route guards don't trigger
- App stuck on protected screens despite being logged out
Environment
- Library: react-native-oidc-auth
- Affected platforms: iOS, Android, Expo
- Affected implementations: Both bare React Native and Expo
- Context: Any app using route guards or protected routes (Expo Router, React Navigation)
Related Code References
packages/react-native-oidc-auth-core/src/provider/oidc-auth/oidc-auth.tsx (line 46-56: event handler)
packages/react-native-oidc-auth-core/src/oidc-auth/oidc-auth.ts (line 427: updateToken throws)
packages/react-native-oidc-auth-core/src/oidc-auth/oidc-auth.ts (line 481: clearToken call)
packages/react-native-oidc-auth-core/src/oidc-auth/oidc-auth.ts (line 487: REFRESH_ERROR emission)
packages/react-native-oidc-auth-core/src/oidc-auth/enum/event-state.ts (EventState enum)
🚨 CRITICAL Bug Report
Severity: HIGH - App gets stuck in authenticated state, preventing route guards from working
Description
When the refresh token expires,
isAuthenticatednever changes tofalse, leaving the app stuck in an authenticated state. This breaks protected routes and auth guards. Additionally, users encounter a JSON parse error fromgetUserInfo.User Impact
Error Message
Root Cause
Two critical issues in the provider's state change handler:
Issue 1: Unhandled Exception Blocks State Update
In
packages/react-native-oidc-auth-core/src/provider/oidc-auth/oidc-auth.tsx(lines 46-56):What happens:
TOKEN_EXPIREDevent firesinstance.updateToken()is calledupdateToken()throws an error (line 427 inoidc-auth.ts)setIsAuthenticated()from being calledisAuthenticated = true❌Issue 2: Missing REFRESH_ERROR Event Handler
When
updateToken()doesn't throw but the refresh fails:clearToken()is called, settingauthenticated = falseinternallyREFRESH_ERRORevent is emitted (line 487 inoidc-auth.ts)REFRESH_ERRORevent - only handlesTOKEN_EXPIREDandINIT_COMPLETEDisAuthenticated = true❌Code Location
File:
packages/react-native-oidc-auth-core/src/provider/oidc-auth/oidc-auth.tsxProblem 1 (lines 46-56): No error handling for
updateToken()Problem 2 (lines 46-56): Missing handler for
REFRESH_ERROReventExpected Behavior
isAuthenticatedshould reliably change tofalseActual Behavior
isAuthenticatednever changes when refresh token expiresProposed Solution
Fix both issues with proper error handling and event listening:
Additional improvement - Clean up user state:
Optionally - Better error messages in getUserInfo implementations:
In both
oidc-auth-bare.tsandoidc-auth-expo.ts, wrap the getUserInfo calls in try-catch to provide clearer error messages about token expiration.Impact
Reproduction Steps
isAuthenticatedremainstrue, route guards don't triggerEnvironment
Related Code References
packages/react-native-oidc-auth-core/src/provider/oidc-auth/oidc-auth.tsx(line 46-56: event handler)packages/react-native-oidc-auth-core/src/oidc-auth/oidc-auth.ts(line 427: updateToken throws)packages/react-native-oidc-auth-core/src/oidc-auth/oidc-auth.ts(line 481: clearToken call)packages/react-native-oidc-auth-core/src/oidc-auth/oidc-auth.ts(line 487: REFRESH_ERROR emission)packages/react-native-oidc-auth-core/src/oidc-auth/enum/event-state.ts(EventState enum)