Back to Notes
authperformancedebugging

Fixing Auth Race Conditions in Production

December 20, 20258 min read

The Bug That Only Happens in Production

Your app works perfectly in development. But in production, users randomly get logged out. Sound familiar?

Common Causes

1. **Token refresh timing**: Multiple requests try to refresh simultaneously
2. **Race between storage and API**: Token used before it's saved
3. **Stale closures**: Old token values captured in callbacks

Debugging Strategy

#

Step 1: Add Comprehensive Logging

```typescript
const refreshToken = async () => {
console.log('[Auth] Refresh started at:', Date.now())
console.log('[Auth] Current token expiry:', tokenExpiry)
// ... refresh logic
console.log('[Auth] Refresh completed at:', Date.now())
}
```

#

Step 2: Implement Token Refresh Lock

```typescript
let isRefreshing = false
let refreshSubscribers: ((token: string) => void)[] = []

const refreshTokenWithLock = async () => {
if (isRefreshing) {
return new Promise((resolve) => {
refreshSubscribers.push(resolve)
})
}

isRefreshing = true
try {
const newToken = await refreshToken()
refreshSubscribers.forEach((cb) => cb(newToken))
refreshSubscribers = []
return newToken
} finally {
isRefreshing = false
}
}
```

#

Step 3: Use Atomic Storage Operations

```typescript
// Bad: Race condition possible
await AsyncStorage.setItem('accessToken', newAccessToken)
await AsyncStorage.setItem('refreshToken', newRefreshToken)

// Good: Atomic multi-set
await AsyncStorage.multiSet([
['accessToken', newAccessToken],
['refreshToken', newRefreshToken],
])
```

The Fix That Worked

After implementing these patterns in the Inbo app, auth-related crashes dropped by 99.7%.

Key changes:
1. Single refresh queue with promise deduplication
2. Atomic token storage with multiSet
3. Request retry with fresh token on 401
4. Proactive refresh 5 minutes before expiry