-
Notifications
You must be signed in to change notification settings - Fork 8
"Untauthorized" fixed on tabbing back #2185
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Signed-off-by: Dian Deskov <[email protected]>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2185 +/- ##
=======================================
Coverage 99.85% 99.85%
=======================================
Files 176 176
Lines 6763 6763
Branches 1301 1301
=======================================
Hits 6753 6753
Misses 10 10 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request fixes the "Unauthorized" errors that occur when users tab away from the application and return after their JWT tokens have expired. The solution adds automatic token validation and re-authentication when the app regains focus.
Changes:
- New composable
useAppVisibilitythat listens for visibility changes and validates organization tokens - Exposed
triggerReauthenticationmethod inAutoLoginInOrganizationcomponent for programmatic re-authentication - Integration of the composable in
GlobalAppProcessesto handle token expiration on app focus
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| front-end/src/renderer/composables/useAppVisibility.ts | New composable that monitors document visibility changes, checks token validity when app becomes visible, and triggers re-authentication callbacks when tokens are expired |
| front-end/src/renderer/components/Organization/AutoLoginInOrganization.vue | Exposed triggerReauthentication method to allow external triggering of the password modal for organization re-authentication |
| front-end/src/renderer/components/GlobalAppProcesses/GlobalAppProcesses.vue | Integrated the useAppVisibility composable with a callback that triggers re-authentication via the AutoLoginInOrganization component reference |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| await user.refetchOrganizationTokens(); | ||
|
|
||
| if (!tokensValid) { | ||
| await user.refetchOrganizations(); | ||
|
|
||
| if (onTokenExpired) { | ||
| await onTokenExpired(); | ||
| } |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The call to refetchOrganizationTokens is redundant when tokens are invalid. The refetchOrganizations function on line 60 already calls refetchOrganizationTokens internally, so calling it on line 57 before checking tokensValid results in fetching tokens twice when they are invalid. Consider moving the refetchOrganizationTokens call into an else block to only execute when tokens are valid.
| await user.refetchOrganizationTokens(); | |
| if (!tokensValid) { | |
| await user.refetchOrganizations(); | |
| if (onTokenExpired) { | |
| await onTokenExpired(); | |
| } | |
| if (!tokensValid) { | |
| await user.refetchOrganizations(); | |
| if (onTokenExpired) { | |
| await onTokenExpired(); | |
| } | |
| } else { | |
| await user.refetchOrganizationTokens(); |
| await user.refetchOrganizations(); | ||
|
|
||
| if (onTokenExpired) { | ||
| await onTokenExpired(); |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a potential race condition where openPasswordModalIfRequired could be called twice when tokens expire. The sequence is: refetchOrganizations triggers the watcher which sets checked to true and calls openPasswordModalIfRequired, then onTokenExpired is called which triggers triggerReauthentication, setting checked to false and calling openPasswordModalIfRequired again. This could result in duplicate password prompts or authentication attempts.
| await user.refetchOrganizations(); | |
| if (onTokenExpired) { | |
| await onTokenExpired(); | |
| if (onTokenExpired) { | |
| // Delegate token expiry handling to the provided callback to avoid | |
| // triggering re-authentication flows twice (e.g., via refetchOrganizations watcher) | |
| await onTokenExpired(); | |
| } else { | |
| await user.refetchOrganizations(); |
Problem
When users tab away from the app and return later, all API requests fail with "Unauthorized" toast errors. The JWT token expires while the app is idle, but there's no mechanism to detect this and trigger re-authentication when the app regains focus.
What Happens:
JwtBlacklistGuardback-end/apps/api/src/guards/jwt-blacklist.guard.ts{ message: 'Unauthorized', statusCode: 401 }Why It Happens After Tabbing Away:
The app has no window focus or visibility change handlers. I searched the entire frontend codebase:
The only auto-login logic is in AutoLoginInOrganization.vue but it only runs once when organizations are initially loaded - not when the app regains focus.
Solution
Add a visibility change listener that validates tokens when the app becomes visible and triggers re-authentication if needed.
Implementation:
Creates a composable that:
document.visibilitychangeeventsChanges:
defineExpose()to expose atriggerReauthentication()methodopenPasswordModalIfRequired()Changes: