diff --git a/browser-extensions/chrome/src/app/components/GlobalStateChangeHandler.tsx b/browser-extensions/chrome/src/app/components/GlobalStateChangeHandler.tsx index 3cb090cc3..614f51559 100644 --- a/browser-extensions/chrome/src/app/components/GlobalStateChangeHandler.tsx +++ b/browser-extensions/chrome/src/app/components/GlobalStateChangeHandler.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useRef } from 'react'; import { useNavigate } from 'react-router-dom'; import { useAuth } from '../context/AuthContext'; @@ -9,13 +9,15 @@ import { useAuth } from '../context/AuthContext'; const GlobalStateChangeHandler: React.FC = () => { const authContext = useAuth(); const navigate = useNavigate(); + const lastLoginState = useRef(authContext.isLoggedIn); /** * Listen for auth logged in changes and redirect to home page if logged in state changes to handle logins and logouts. */ useEffect(() => { - // Only navigate when auth state changes and we're not already on home page - if (window.location.pathname !== '/index.html' && window.location.pathname !== '/') { + // Only navigate when auth state is different from the last state we acted on. + if (lastLoginState.current !== authContext.isLoggedIn) { + lastLoginState.current = authContext.isLoggedIn; navigate('/'); } }, [authContext.isLoggedIn]); // eslint-disable-line react-hooks/exhaustive-deps diff --git a/browser-extensions/chrome/src/app/pages/CredentialsList.tsx b/browser-extensions/chrome/src/app/pages/CredentialsList.tsx index 18c82a3e8..e53ee24cc 100644 --- a/browser-extensions/chrome/src/app/pages/CredentialsList.tsx +++ b/browser-extensions/chrome/src/app/pages/CredentialsList.tsx @@ -10,7 +10,6 @@ import ReloadButton from '../components/ReloadButton'; import { useAuth } from '../context/AuthContext'; import LoadingSpinner from '../components/LoadingSpinner'; import { useMinDurationLoading } from '../hooks/useMinDurationLoading'; -import { AppInfo } from '../../shared/AppInfo'; /** * Credentials list page. @@ -38,15 +37,9 @@ const CredentialsList: React.FC = () => { // Do status check first to ensure the extension is (still) supported. const statusResponse = await webApi.getStatus(); - console.log('CredentialsList: statusResponse', statusResponse); - if (!statusResponse.clientVersionSupported) { - authContext.logout('This version of the AliasVault browser extension is outdated. Please update your browser extension to the latest version.'); - return; - } - - // Check if server version is supported by this client. - if (!AppInfo.isServerVersionSupported(statusResponse.serverVersion)) { - authContext.logout('The AliasVault server needs to be updated to a newer version in order to use this browser extension. Please contact support if you need help.'); + const statusError = webApi.validateStatusResponse(statusResponse); + if (statusError !== null) { + authContext.logout(statusError); return; } diff --git a/browser-extensions/chrome/src/app/pages/Home.tsx b/browser-extensions/chrome/src/app/pages/Home.tsx index 81ff82c7d..e2995e7a9 100644 --- a/browser-extensions/chrome/src/app/pages/Home.tsx +++ b/browser-extensions/chrome/src/app/pages/Home.tsx @@ -38,29 +38,20 @@ const Home: React.FC = () => { // Show loading state if not fully initialized or when about to redirect to credentials. if (!isFullyInitialized || (isFullyInitialized && !requireLoginOrUnlock)) { // Global loading spinner will be shown by the parent component. - console.log('Home: not fully initialized'); return null; } setIsInitialLoading(false); if (!isAuthenticated) { - console.log('Home: not authenticated'); return ; } if (!isDatabaseAvailable) { - console.log('isFullyInitialized', isFullyInitialized); - console.log('isAuthenticated', isAuthenticated); - console.log('isDatabaseAvailable', isDatabaseAvailable); - console.log('isInlineUnlockMode', isInlineUnlockMode); - console.log('requireLoginOrUnlock', requireLoginOrUnlock); - console.log('Home: not database available'); return ; } if (isInlineUnlockMode) { - console.log('Home: inline unlock mode'); return setIsInlineUnlockMode(false)} />; } diff --git a/browser-extensions/chrome/src/app/pages/Login.tsx b/browser-extensions/chrome/src/app/pages/Login.tsx index c4f12058c..62f582f57 100644 --- a/browser-extensions/chrome/src/app/pages/Login.tsx +++ b/browser-extensions/chrome/src/app/pages/Login.tsx @@ -167,7 +167,6 @@ const Login: React.FC = () => { return; } - // All is good. Store auth info which is required to make requests to the web API. await authContext.setAuthTokens(credentials.username, validationResponse.token.token, validationResponse.token.refreshToken); diff --git a/browser-extensions/chrome/src/app/pages/Unlock.tsx b/browser-extensions/chrome/src/app/pages/Unlock.tsx index a8c25055b..34b286d78 100644 --- a/browser-extensions/chrome/src/app/pages/Unlock.tsx +++ b/browser-extensions/chrome/src/app/pages/Unlock.tsx @@ -28,9 +28,11 @@ const Unlock: React.FC = () => { * Make status call to API which acts as health check. */ const checkStatus = async () : Promise => { - const status = await webApi.getStatus(); - if (!status.clientVersionSupported) { - authContext.logout('The browser extension is outdated. Please update to the latest version.'); + const statusResponse = await webApi.getStatus(); + const statusError = webApi.validateStatusResponse(statusResponse); + if (statusError !== null) { + authContext.logout(statusError); + return; } }; diff --git a/browser-extensions/chrome/src/background/VaultMessageHandler.ts b/browser-extensions/chrome/src/background/VaultMessageHandler.ts index 95d97dcb5..22d3dbfde 100644 --- a/browser-extensions/chrome/src/background/VaultMessageHandler.ts +++ b/browser-extensions/chrome/src/background/VaultMessageHandler.ts @@ -45,10 +45,10 @@ export async function handleSyncVault( sendResponse: (response: any) => void ) : Promise { const webApi = new WebApiService(() => {}); - const response = await webApi.getStatus(); - - if (!response.clientVersionSupported) { - sendResponse({ success: false, error: 'The browser extension is outdated. Please update to the latest version.' }); + const statusResponse = await webApi.getStatus(); + const statusError = webApi.validateStatusResponse(statusResponse); + if (statusError !== null) { + sendResponse({ success: false, error: statusError }); return; } @@ -56,7 +56,7 @@ export async function handleSyncVault( 'vaultRevisionNumber' ]); - if (response.vaultRevision > result.vaultRevisionNumber) { + if (statusResponse.vaultRevision > result.vaultRevisionNumber) { // Retrieve the latest vault from the server. const vaultResponse = await webApi.get('Vault'); diff --git a/browser-extensions/chrome/src/shared/AppInfo.ts b/browser-extensions/chrome/src/shared/AppInfo.ts index 32453e0de..c79576321 100644 --- a/browser-extensions/chrome/src/shared/AppInfo.ts +++ b/browser-extensions/chrome/src/shared/AppInfo.ts @@ -2,14 +2,20 @@ * AppInfo class which contains information about the application version. */ export class AppInfo { - // Current extension version - should be updated with each release. + /** + * The current extension version. This should be updated with each release of the extension. + */ public static readonly VERSION = '0.12.0'; - // Minimum supported AliasVault server (API) version. If the server version is below this, the - // client will throw an error stating that the server should be updated. - public static readonly MIN_SERVER_VERSION = '0.13.0'; + /** + * The minimum supported AliasVault server (API) version. If the server version is below this, the + * client will throw an error stating that the server should be updated. + */ + public static readonly MIN_SERVER_VERSION = '0.12.0-dev'; - // Minimum supported AliasVault client vault version. + /** + * The minimum supported AliasVault client vault version. + */ public static readonly MIN_VAULT_VERSION = '1.4.1'; /* @@ -65,8 +71,7 @@ export class AppInfo { if (part1 < part2) return false; } - // If core versions are equal, check pre-release versions - // No pre-release > pre-release + // If core versions are equal, check pre-release versions. if (!preRelease1 && preRelease2) return true; if (preRelease1 && !preRelease2) return false; if (!preRelease1 && !preRelease2) return true; diff --git a/browser-extensions/chrome/src/shared/WebApiService.ts b/browser-extensions/chrome/src/shared/WebApiService.ts index 99611754e..93eac7714 100644 --- a/browser-extensions/chrome/src/shared/WebApiService.ts +++ b/browser-extensions/chrome/src/shared/WebApiService.ts @@ -218,6 +218,21 @@ export class WebApiService { return await this.get('Auth/status'); } + /** + * Validates the status response and returns an error message if validation fails. + */ + public validateStatusResponse(statusResponse: StatusResponse): string | null { + if (!statusResponse.clientVersionSupported) { + return 'This version of the AliasVault browser extension is outdated. Please update your browser extension to the latest version.'; + } + + if (!AppInfo.isServerVersionSupported(statusResponse.serverVersion)) { + return 'The AliasVault server needs to be updated to a newer version in order to use this browser extension. Please contact support if you need help.'; + } + + return null; + } + /** * Validates the vault response and returns an error message if validation fails */ diff --git a/browser-extensions/chrome/src/shared/__tests__/AppInfo.test.ts b/browser-extensions/chrome/src/shared/__tests__/AppInfo.test.ts index 5c09cb06c..caa6f464c 100644 --- a/browser-extensions/chrome/src/shared/__tests__/AppInfo.test.ts +++ b/browser-extensions/chrome/src/shared/__tests__/AppInfo.test.ts @@ -1,7 +1,6 @@ import { AppInfo } from '../AppInfo'; import { describe, it, expect } from 'vitest'; - describe('AppInfo', () => { describe('isVersionSupported', () => { it('should support exact version match', () => { diff --git a/src/Shared/AliasVault.Shared.Core/AppInfo.cs b/src/Shared/AliasVault.Shared.Core/AppInfo.cs index 8361817a1..33a40564b 100644 --- a/src/Shared/AliasVault.Shared.Core/AppInfo.cs +++ b/src/Shared/AliasVault.Shared.Core/AppInfo.cs @@ -25,12 +25,12 @@ public static class AppInfo /// /// Gets the minor version number. /// - public const int VersionMinor = 11; + public const int VersionMinor = 12; /// /// Gets the patch version number. /// - public const int VersionPatch = 1; + public const int VersionPatch = 0; /// /// Gets a dictionary of minimum supported client versions that the WebApi supports.