Skip to content

fix(auth): prevent unexpected logout during file upload#48

Merged
marevol merged 3 commits into
mainfrom
fix/file-upload-token-refresh
Feb 19, 2026
Merged

fix(auth): prevent unexpected logout during file upload#48
marevol merged 3 commits into
mainfrom
fix/file-upload-token-refresh

Conversation

@marevol

@marevol marevol commented Feb 19, 2026

Copy link
Copy Markdown
Collaborator

Summary

Users were being unexpectedly "logged out" while uploading files. The root cause: DataUploadPage uses raw XMLHttpRequest which bypasses the API client's automatic token refresh. If the access token expired between page load and upload completion, the XHR returned 401 — which surfaced as an upload error that looked like a logout.

Changes Made

frontend/src/stores/auth.ts

  • ensureFreshToken(): New exported method that checks if the access token expires within 60 seconds and proactively refreshes it. Returns true if a valid token is available after the check, false if not authenticated.
  • visibilitychange listener: When a browser tab regains focus, re-invokes scheduleProactiveRefresh(). Since scheduleProactiveRefresh already handles delay <= 0 by refreshing immediately, this naturally handles tokens that expired while the tab was backgrounded (where setTimeout was throttled by the browser).

frontend/src/pages/DataUploadPage.vue

  • Pre-upload token check: Calls authStore.ensureFreshToken() before starting the XHR, ensuring the token is fresh at upload time.
  • 401 retry: If the XHR returns 401 despite the pre-check (e.g., token expired during a long upload), refreshes the token and retries the upload once.
  • Error status: Attaches .status to XHR errors so the retry logic can identify 401s specifically (vs. 400, 403, 500, etc.).

frontend/src/__tests__/stores/auth.test.ts & DataUploadPage.test.ts

  • Updated nextTickflushPromises after emitUploader calls (needed because ensureFreshToken is now async)
  • Added 23 new tests covering: ensureFreshToken (8 cases), visibilitychange (3 cases), pre-upload refresh (2 cases), 401 retry (8 cases), error status property (2 cases)

Testing

  • All 607 frontend unit tests pass (npm run test:unit)
  • vue-tsc --noEmit passes with no type errors
  • Manual repro scenario: set ACCESS_TOKEN_LIFETIME=60, login, wait ~55s, upload a file → upload succeeds instead of showing a 401 error

Breaking Changes

None. ensureFreshToken is additive; existing behaviour of refreshAccessToken and scheduleProactiveRefresh is unchanged.

Additional Notes

  • The 401 retry only fires once — a second failure is shown as an error rather than looping
  • Retry does NOT fire for 400, 403, or 5xx responses (only 401), avoiding unintended retries on validation errors
  • The visibilitychange listener is registered once per store instance; since the auth store is a singleton via Pinia, there's no risk of duplicate listeners

marevol and others added 3 commits February 19, 2026 12:13
… tasks

Add new test modules for management commands (assign_owners, create_api_key,
create_test_users), project service, and user service. Extend existing tests
for authentication (scope permissions, ambiguous prefix), data upload
(preview endpoint, cross-owner access), event views (API key access control,
slot/project mismatch), serializers, schedule service, and Celery tasks
(training, tuning, scheduled retraining, error handling).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ctively

- Add ensureFreshToken() to auth store: checks token expiry and refreshes
  if within 60 seconds of expiry, preventing stale-token upload failures
- Add visibilitychange listener: re-schedules proactive refresh when the
  tab regains focus, covering tokens that expired in background tabs
- Add 401 retry in DataUploadPage: if the XHR returns 401 (token expired
  mid-upload), refresh the token and retry the upload once automatically
- Attach .status to XHR errors so the 401 can be detected in catch blocks
- Update tests: switch nextTick → flushPromises for async ensureFreshToken,
  add 23 new tests covering all new behaviours

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@marevol marevol merged commit fa511ba into main Feb 19, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant