fix: prevent unwanted login redirects during WebSocket reconnection (#6410)

## 🐛 Problem

Users were experiencing the following issues during WebSocket
reconnection:

1. Automatic redirect to login page after "Reconnecting" toast message
appears
2. Automatic re-login after a few seconds, returning to the main
interface
3. This cycle repeats, severely degrading user experience

## 🔍 Root Cause Analysis

### 1. Router Guard Catching Too Many Errors
```typescript
// Problematic code
try {
  const { getUserCloudStatus, getSurveyCompletedStatus } = await import('@/api/auth')
  const userStatus = await getUserCloudStatus()
  // ...
} catch (error) {
  // All types of errors are caught here
  return next({ name: 'cloud-user-check' })
}
```

With dynamic import inside the try block, the following were all being
caught:
- Errors during `@/api/auth` module loading
- Runtime errors from the API singleton
- Actual API call errors

Everything was caught and redirected to `cloud-user-check`.

### 2. Full Page Reload in UserCheckView
```typescript
// Problematic code
window.location.href = '/'  // Full page reload!
```

This caused:
- Loss of SPA benefits
- Firebase Auth re-initialization → temporarily null user
- Router guard re-execution → potential for another redirect

##  Solution

### 1. router.ts: Move dynamic import outside try block
```typescript
// After fix
const { getUserCloudStatus, getSurveyCompletedStatus } = await import('@/api/auth')

try {
  // Only API calls inside try
  const userStatus = await getUserCloudStatus()
  // ...
} catch (error) {
  // Now only catches pure API call errors
  return next({ name: 'cloud-user-check' })
}
```

### 2. UserCheckView.vue: Use SPA routing
```typescript
// After fix
await router.replace('/')  // Use Vue Router instead of window.location.href
```

🤖 Generated with [Claude Code](https://claude.ai/code)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6410-fix-prevent-unwanted-login-redirects-during-WebSocket-reconnection-29c6d73d3650818a8a1acbdcebd2f703)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
Jin Yi
2025-10-31 11:16:22 +09:00
committed by GitHub
parent 750a9d882a
commit b575a8d7a2
5 changed files with 21 additions and 13 deletions

View File

@@ -1,5 +1,5 @@
import { computed, ref, watch } from 'vue'
import { createSharedComposable } from '@vueuse/core'
import { computed, ref, watch } from 'vue'
import { useCurrentUser } from '@/composables/auth/useCurrentUser'
import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'

View File

@@ -74,7 +74,7 @@ const {
}
// User is fully onboarded
window.location.href = '/'
await router.replace('/')
}),
null,
{ resetOnExecute: false }

View File

@@ -183,12 +183,11 @@ router.beforeEach(async (to, _from, next) => {
// User is logged in - check if they need onboarding
// For root path, check actual user status to handle waitlisted users
if (!isElectron() && isLoggedIn && to.path === '/') {
// Import auth functions dynamically to avoid circular dependency
const { getUserCloudStatus, getSurveyCompletedStatus } = await import(
'@/api/auth'
)
try {
// Import auth functions dynamically to avoid circular dependency
const { getUserCloudStatus, getSurveyCompletedStatus } = await import(
'@/api/auth'
)
// Check user's actual status
const userStatus = await getUserCloudStatus()
const surveyCompleted = await getSurveyCompletedStatus()

View File

@@ -433,8 +433,12 @@ export class ComfyApi extends EventTarget {
await this.#waitForAuthInitialization()
// Add Firebase JWT token if user is logged in
// Force refresh token on reconnection to avoid 401 errors
const isReconnecting =
options.headers && 'X-Reconnecting' in options.headers
try {
const authHeader = await useFirebaseAuthStore().getAuthHeader()
const authHeader =
await useFirebaseAuthStore().getAuthHeader(isReconnecting)
if (authHeader) {
if (Array.isArray(options.headers)) {
for (const [key, value] of Object.entries(authHeader)) {
@@ -542,9 +546,10 @@ export class ComfyApi extends EventTarget {
let existingSession = window.name
// Get auth token if available
// Force refresh on reconnect to avoid stale tokens
let authToken: string | undefined
try {
authToken = await useFirebaseAuthStore().getIdToken()
authToken = await useFirebaseAuthStore().getIdToken(isReconnect)
} catch (error) {
// Continue without auth token if there's an error
console.warn('Could not get auth token for WebSocket connection:', error)

View File

@@ -106,10 +106,12 @@ export const useFirebaseAuthStore = defineStore('firebaseAuth', () => {
}
})
const getIdToken = async (): Promise<string | undefined> => {
const getIdToken = async (
forceRefresh = false
): Promise<string | undefined> => {
if (!currentUser.value) return
try {
return await currentUser.value.getIdToken()
return await currentUser.value.getIdToken(forceRefresh)
} catch (error: unknown) {
if (
error instanceof FirebaseError &&
@@ -140,9 +142,11 @@ export const useFirebaseAuthStore = defineStore('firebaseAuth', () => {
* - An ApiKeyAuthHeader with X-API-KEY if API key exists
* - null if neither authentication method is available
*/
const getAuthHeader = async (): Promise<AuthHeader | null> => {
const getAuthHeader = async (
forceRefresh = false
): Promise<AuthHeader | null> => {
// If available, set header with JWT used to identify the user to Firebase service
const token = await getIdToken()
const token = await getIdToken(forceRefresh)
if (token) {
return {
Authorization: `Bearer ${token}`