From ea889e0002a2f8050529aa6cf70a9446d2f9842a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jim=C3=A9nez=20Rivera?= Date: Wed, 11 Feb 2026 10:07:34 +0100 Subject: [PATCH 1/8] Check refresh token on startup --- src/apps/main/auth/refresh-token.ts | 13 ------- src/apps/main/auth/service.ts | 14 +------ src/apps/main/config.ts | 1 - .../main/token-scheduler/TokenScheduler.ts | 39 ++++++++++++------- .../auth/services/utils/reset-config.test.ts | 1 - .../electron/store/app-store.interface.ts | 1 - src/core/electron/store/defaults.ts | 3 +- .../watcher/tests/watcher-on-add.test.ts | 11 ++++++ 8 files changed, 38 insertions(+), 45 deletions(-) delete mode 100644 src/apps/main/auth/refresh-token.ts diff --git a/src/apps/main/auth/refresh-token.ts b/src/apps/main/auth/refresh-token.ts deleted file mode 100644 index e77c68b395..0000000000 --- a/src/apps/main/auth/refresh-token.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { updateCredentials } from './service'; -import { driveServerWipModule } from '@/infra/drive-server-wip/drive-server-wip.module'; - -export async function refreshToken() { - const { data } = await driveServerWipModule.auth.refresh(); - - if (data) { - updateCredentials({ newToken: data.newToken }); - return true; - } else { - return false; - } -} diff --git a/src/apps/main/auth/service.ts b/src/apps/main/auth/service.ts index c99ddd1a22..11fb196bce 100644 --- a/src/apps/main/auth/service.ts +++ b/src/apps/main/auth/service.ts @@ -6,15 +6,6 @@ const TOKEN_ENCODING = 'latin1'; export function obtainToken(): string { const token = ConfigStore.get('newToken'); - const isEncrypted = ConfigStore.get('newTokenEncrypted'); - - if (!isEncrypted) { - return token; - } - - if (!safeStorage.isEncryptionAvailable()) { - throw new Error('[AUTH] Safe Storage was not available when decrypting encrypted token'); - } const buffer = Buffer.from(token, TOKEN_ENCODING); @@ -35,12 +26,9 @@ export function setUser(userData: User) { } export function updateCredentials({ newToken }: { newToken: string }) { - const isSafeStorageAvailable = safeStorage.isEncryptionAvailable(); - - const token = isSafeStorageAvailable ? ecnryptToken(newToken) : newToken; + const token = ecnryptToken(newToken); ConfigStore.set('newToken', token); - ConfigStore.set('newTokenEncrypted', isSafeStorageAvailable); } export function getUser(): User | null { diff --git a/src/apps/main/config.ts b/src/apps/main/config.ts index e9a8ddced6..2392afdd91 100644 --- a/src/apps/main/config.ts +++ b/src/apps/main/config.ts @@ -11,7 +11,6 @@ const schema: Schema = { backupList: { type: 'object' }, newToken: { type: 'string' }, - newTokenEncrypted: { type: 'boolean' }, userData: { type: 'object' }, mnemonic: { type: 'string' }, diff --git a/src/apps/main/token-scheduler/TokenScheduler.ts b/src/apps/main/token-scheduler/TokenScheduler.ts index 436d8aaa46..0877a320e0 100644 --- a/src/apps/main/token-scheduler/TokenScheduler.ts +++ b/src/apps/main/token-scheduler/TokenScheduler.ts @@ -1,39 +1,50 @@ import { logger } from '@/apps/shared/logger/logger'; import jwtDecode, { JwtPayload } from 'jwt-decode'; -import { refreshToken } from '../auth/refresh-token'; -import { obtainToken } from '../auth/service'; +import { obtainToken, updateCredentials } from '../auth/service'; +import { driveServerWip } from '@/infra/drive-server-wip/drive-server-wip.module'; const DAYS_BEFORE = 1; export class TokenScheduler { - timeout: NodeJS.Timeout | undefined; + private static timeout: NodeJS.Timeout | undefined; - getExpiration() { + static getTimeout() { + return this.timeout; + } + + static getExpiresAt() { const token = obtainToken(); const decoded = jwtDecode(token); if (!decoded.exp) throw new Error('Token does not have expiration time'); return decoded.exp * 1000; } - schedule() { + static getRenewAt() { + const expiresAt = this.getExpiresAt(); + const renewAt = expiresAt - DAYS_BEFORE * 24 * 60 * 60 * 1000; + const msToRenew = renewAt - Date.now(); + return { expiresAt, renewAt, msToRenew }; + } + + static schedule() { try { - const expirationDate = this.getExpiration(); - const renewDate = expirationDate - DAYS_BEFORE * 24 * 60 * 60 * 1000; - const delayInMs = renewDate - Date.now(); + const { expiresAt, renewAt, msToRenew } = this.getRenewAt(); logger.debug({ tag: 'AUTH', msg: 'Token renew date', - expiresAt: new Date(expirationDate), - renewAt: new Date(renewDate), + expiresAt: new Date(expiresAt), + renewAt: new Date(renewAt), }); this.timeout = setTimeout(async () => { - const isRefreshed = await refreshToken(); - if (isRefreshed) { + const { data } = await driveServerWip.auth.refresh(); + + if (data) { + updateCredentials({ newToken: data.newToken }); this.schedule(); } - }, delayInMs); + }, msToRenew); } catch (error) { logger.error({ tag: 'AUTH', @@ -43,7 +54,7 @@ export class TokenScheduler { } } - stop() { + static stop() { clearTimeout(this.timeout); } } diff --git a/src/backend/features/auth/services/utils/reset-config.test.ts b/src/backend/features/auth/services/utils/reset-config.test.ts index 768efbef3a..99423e66b9 100644 --- a/src/backend/features/auth/services/utils/reset-config.test.ts +++ b/src/backend/features/auth/services/utils/reset-config.test.ts @@ -15,7 +15,6 @@ describe('resetConfig', () => { ['deviceUuid', ''], ['backupList', {}], ['newToken', ''], - ['newTokenEncrypted', false], ['userData', {}], ['mnemonic', ''], ]); diff --git a/src/core/electron/store/app-store.interface.ts b/src/core/electron/store/app-store.interface.ts index 2a56cbfa04..6cfbb91442 100644 --- a/src/core/electron/store/app-store.interface.ts +++ b/src/core/electron/store/app-store.interface.ts @@ -19,7 +19,6 @@ export type AppStore = { backupList: BackupList; newToken: string; - newTokenEncrypted: boolean; userData: User; mnemonic: string; diff --git a/src/core/electron/store/defaults.ts b/src/core/electron/store/defaults.ts index e1035eedfe..bcee7ce552 100644 --- a/src/core/electron/store/defaults.ts +++ b/src/core/electron/store/defaults.ts @@ -10,7 +10,6 @@ export const defaults: AppStore = { backupList: {}, newToken: '', - newTokenEncrypted: false, userData: {} as User, mnemonic: '', @@ -25,4 +24,4 @@ export const defaults: AppStore = { }; export const fieldsToSave: Array = ['backupInterval', 'lastBackup', 'syncRoot', 'deviceUuid', 'backupList']; -export const fieldsToReset: Array = ['newToken', 'newTokenEncrypted', 'userData', 'mnemonic']; +export const fieldsToReset: Array = ['newToken', 'userData', 'mnemonic']; diff --git a/src/node-win/watcher/tests/watcher-on-add.test.ts b/src/node-win/watcher/tests/watcher-on-add.test.ts index 816f9c66ad..b1210e9d23 100644 --- a/src/node-win/watcher/tests/watcher-on-add.test.ts +++ b/src/node-win/watcher/tests/watcher-on-add.test.ts @@ -43,6 +43,17 @@ describe('watcher on add', () => { getEvents().toMatchObject([{ event: 'create', path: file, type: 'file' }]); }); + it('should emit create event when create file with strange characters', async () => { + // Given + const file = join(rootPath, 'Леди Баг в стиле куклы Лол'); + await setupWatcher(rootPath); + // When + await writeFile(file, ''); + await sleep(50); + // Then + getEvents().toMatchObject([{ event: 'create', path: file, type: 'file' }]); + }); + it('should emit create and update events when create file inside a folder', async () => { // Given const file = join(parent, 'file'); From 1cf1aa5c54c9622ff2c14ef316835d5b0cb84be0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jim=C3=A9nez=20Rivera?= Date: Wed, 11 Feb 2026 10:07:44 +0100 Subject: [PATCH 2/8] Update launchBackupProcesses.ts --- .../background-processes/backups/launchBackupProcesses.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/apps/main/background-processes/backups/launchBackupProcesses.ts b/src/apps/main/background-processes/backups/launchBackupProcesses.ts index b4819cb903..6479ac8bcd 100644 --- a/src/apps/main/background-processes/backups/launchBackupProcesses.ts +++ b/src/apps/main/background-processes/backups/launchBackupProcesses.ts @@ -110,4 +110,9 @@ export async function launchBackupProcesses({ ctx }: Props) { ipcMain.removeAllListeners('stop-backups-process'); powerSaveBlocker.stop(suspensionBlockId); + + logger.debug({ + tag: 'BACKUPS', + msg: 'Backup finished', + }); } From 53828e2a5fc8d08f8655716bc4dd10792d8f4a2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jim=C3=A9nez=20Rivera?= Date: Wed, 11 Feb 2026 10:07:55 +0100 Subject: [PATCH 3/8] Update token-scheduler.test.ts --- .../main/token-scheduler/token-scheduler.test.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/apps/main/token-scheduler/token-scheduler.test.ts b/src/apps/main/token-scheduler/token-scheduler.test.ts index b4c0cc2de4..eeee7c95e8 100644 --- a/src/apps/main/token-scheduler/token-scheduler.test.ts +++ b/src/apps/main/token-scheduler/token-scheduler.test.ts @@ -15,8 +15,6 @@ function createToken(expiresIn: StringValue) { describe('Token Scheduler', () => { const obtainTokenMock = partialSpyOn(obtainToken, 'obtainToken'); - const scheduler = new TokenScheduler(); - const jwtWithoutExpiration = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwYXlsb2FkIjp7InV1aWQiOiIzMjE2YzUzNi1kZDJjLTVhNjEtOGM3Ni0yMmU0ZDQ4ZjY4OWUiLCJlbWFpbCI6InRlc3RAaW50ZXJueHQuY29tIiwibmFtZSI6InRlc3QiLCJsYXN0bmFtZSI6InRlc3QiLCJ1c2VybmFtZSI6InRlc3RAaW50ZXJueHQuY29tIiwic2hhcmVkV29ya3NwYWNlIjp0cnVlLCJuZXR3b3JrQ3JlZGVudGlhbHMiOnsidXNlciI6InRlc3RAaW50ZXJueHQuY29tIiwicGFzcyI6IiQyYSQwOCQ2QmhjZkRxaDE4c0kwN25kb2x0N29PNEtaTkpVQmpXSzYvZTRxMWppclR2SzdOTWE4dmZpLiJ9fSwiaWF0IjoxNjY3ODI4MDA2fQ.ckwjRsdNu9UUKUtdO3G32SwUUoMj7FAAOuBqVsIemo0'; @@ -33,9 +31,9 @@ describe('Token Scheduler', () => { // Given obtainTokenMock.mockReturnValue(jwtWithoutExpiration); // When - scheduler.schedule(); + TokenScheduler.schedule(); // Then - expect(scheduler.timeout).toBe(undefined); + expect(TokenScheduler.getTimeout()).toBe(undefined); call(loggerMock.error).toMatchObject({ error: new Error('Token does not have expiration time'), msg: 'Error scheduling refresh token', @@ -46,9 +44,9 @@ describe('Token Scheduler', () => { // Given obtainTokenMock.mockReturnValue('invalid'); // When - scheduler.schedule(); + TokenScheduler.schedule(); // Then - expect(scheduler.timeout).toBe(undefined); + expect(TokenScheduler.getTimeout()).toBe(undefined); call(loggerMock.error).toMatchObject({ msg: 'Error scheduling refresh token' }); }); @@ -56,9 +54,9 @@ describe('Token Scheduler', () => { // Given obtainTokenMock.mockReturnValue(createToken('31 day')); // When - scheduler.schedule(); + TokenScheduler.schedule(); // Then - expect(scheduler.timeout).not.toBe(undefined); + expect(TokenScheduler.getTimeout()).not.toBe(undefined); call(loggerMock.debug).toMatchObject({ msg: 'Token renew date', expiresAt: new Date('1970-02-01'), From 9bda9929c0778873965b3a2985b2ef7b36a4bd43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jim=C3=A9nez=20Rivera?= Date: Wed, 11 Feb 2026 10:08:03 +0100 Subject: [PATCH 4/8] Update handlers.ts --- src/apps/main/auth/handlers.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/apps/main/auth/handlers.ts b/src/apps/main/auth/handlers.ts index b326b1e7cf..d44d32e795 100644 --- a/src/apps/main/auth/handlers.ts +++ b/src/apps/main/auth/handlers.ts @@ -1,7 +1,6 @@ import { ipcMain } from 'electron'; import eventBus from '../event-bus'; import { getWidget } from '../windows/widget'; -import { refreshToken } from './refresh-token'; import { getUser } from './service'; import { logger } from '@/apps/shared/logger/logger'; import { cleanAndStartRemoteNotifications } from '../realtime'; @@ -32,7 +31,7 @@ export function onUserUnauthorized() { eventBus.emit('USER_LOGGED_OUT'); } -export async function checkIfUserIsLoggedIn() { +export function checkIfUserIsLoggedIn() { const user = getUser(); if (!user) { @@ -45,7 +44,13 @@ export async function checkIfUserIsLoggedIn() { return false; } - return await refreshToken(); + const { msToRenew } = TokenScheduler.getRenewAt(); + if (msToRenew <= 0) { + logger.debug({ tag: 'AUTH', msg: 'User token is expired' }); + return false; + } + + return true; } export function setupAuthIpcHandlers() { @@ -59,8 +64,7 @@ export function setupAuthIpcHandlers() { export async function emitUserLoggedIn() { logger.debug({ tag: 'AUTH', msg: 'User logged in' }); - const scheduler = new TokenScheduler(); - scheduler.schedule(); + TokenScheduler.schedule(); const abortController = new AbortController(); setMaxListeners(0, abortController.signal); @@ -79,7 +83,7 @@ export async function emitUserLoggedIn() { eventBus.once('USER_LOGGED_OUT', () => { logger.debug({ tag: 'AUTH', msg: 'Received logout event' }); clearLoggedPreloadIpc(); - scheduler.stop(); + TokenScheduler.stop(); BackupScheduler.stop(); logout({ ctx }); }); From 69dfcaed84b0512aa0c9c2cd1e9b510ab222c33d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jim=C3=A9nez=20Rivera?= Date: Wed, 11 Feb 2026 10:25:37 +0100 Subject: [PATCH 5/8] Commit --- src/apps/main/auth/handlers.test.ts | 51 +++++++++++++++++++++++++++++ src/apps/main/main.ts | 2 +- 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 src/apps/main/auth/handlers.test.ts diff --git a/src/apps/main/auth/handlers.test.ts b/src/apps/main/auth/handlers.test.ts new file mode 100644 index 0000000000..09037672cc --- /dev/null +++ b/src/apps/main/auth/handlers.test.ts @@ -0,0 +1,51 @@ +import { partialSpyOn } from '@/tests/vitest/utils.helper.test'; +import { checkIfUserIsLoggedIn } from './handlers'; +import * as getUser from './service'; +import { TokenScheduler } from '../token-scheduler/TokenScheduler'; + +describe('handlers', () => { + const getUserMock = partialSpyOn(getUser, 'getUser'); + const getRenewAtMock = partialSpyOn(TokenScheduler, 'getRenewAt'); + + describe('checkUserIsLoggedIn', () => { + beforeEach(() => { + getUserMock.mockReturnValue({ needLogout: false }); + }); + + it('should return false if user does not exist', () => { + // Given + getUserMock.mockReturnValue(null); + // When + const res = checkIfUserIsLoggedIn(); + // Then + expect(res).toBe(false); + }); + + it('should return false if user needs logout', () => { + // Given + getUserMock.mockReturnValue({ needLogout: undefined }); + // When + const res = checkIfUserIsLoggedIn(); + // Then + expect(res).toBe(false); + }); + + it('should return false if token is expired', () => { + // Given + getRenewAtMock.mockReturnValue({ msToRenew: -1 }); + // When + const res = checkIfUserIsLoggedIn(); + // Then + expect(res).toBe(false); + }); + + it('should return true if token is not expired', () => { + // Given + getRenewAtMock.mockReturnValue({ msToRenew: 100 }); + // When + const res = checkIfUserIsLoggedIn(); + // Then + expect(res).toBe(true); + }); + }); +}); diff --git a/src/apps/main/main.ts b/src/apps/main/main.ts index 392b9a9215..249852ddb0 100644 --- a/src/apps/main/main.ts +++ b/src/apps/main/main.ts @@ -129,7 +129,7 @@ app setUpBackups(); - const isLoggedIn = await checkIfUserIsLoggedIn(); + const isLoggedIn = checkIfUserIsLoggedIn(); if (isLoggedIn) { setIsLoggedIn(true); From 57e9c37c3be0b0a9c4218bb3b95f6577ce1e87f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jim=C3=A9nez=20Rivera?= Date: Wed, 11 Feb 2026 11:53:14 +0100 Subject: [PATCH 6/8] Update token-scheduler.test.ts --- .../token-scheduler/token-scheduler.test.ts | 43 +++++++++++++------ 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/src/apps/main/token-scheduler/token-scheduler.test.ts b/src/apps/main/token-scheduler/token-scheduler.test.ts index eeee7c95e8..8bf410c402 100644 --- a/src/apps/main/token-scheduler/token-scheduler.test.ts +++ b/src/apps/main/token-scheduler/token-scheduler.test.ts @@ -2,9 +2,10 @@ import jwt from 'jsonwebtoken'; import { StringValue } from 'ms'; import { TokenScheduler } from './TokenScheduler'; -import { call, partialSpyOn } from '@/tests/vitest/utils.helper.test'; +import { call, calls, partialSpyOn } from '@/tests/vitest/utils.helper.test'; import * as obtainToken from '../auth/service'; import { loggerMock } from '@/tests/vitest/mocks.helper.test'; +import { driveServerWip } from '@/infra/drive-server-wip/drive-server-wip.module'; function createToken(expiresIn: StringValue) { const email = 'test@internxt.com'; @@ -12,11 +13,10 @@ function createToken(expiresIn: StringValue) { return jwt.sign({ email }, 'JWT_SECRET', { expiresIn }); } -describe('Token Scheduler', () => { +describe('token-scheduler', () => { const obtainTokenMock = partialSpyOn(obtainToken, 'obtainToken'); - - const jwtWithoutExpiration = - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwYXlsb2FkIjp7InV1aWQiOiIzMjE2YzUzNi1kZDJjLTVhNjEtOGM3Ni0yMmU0ZDQ4ZjY4OWUiLCJlbWFpbCI6InRlc3RAaW50ZXJueHQuY29tIiwibmFtZSI6InRlc3QiLCJsYXN0bmFtZSI6InRlc3QiLCJ1c2VybmFtZSI6InRlc3RAaW50ZXJueHQuY29tIiwic2hhcmVkV29ya3NwYWNlIjp0cnVlLCJuZXR3b3JrQ3JlZGVudGlhbHMiOnsidXNlciI6InRlc3RAaW50ZXJueHQuY29tIiwicGFzcyI6IiQyYSQwOCQ2QmhjZkRxaDE4c0kwN25kb2x0N29PNEtaTkpVQmpXSzYvZTRxMWppclR2SzdOTWE4dmZpLiJ9fSwiaWF0IjoxNjY3ODI4MDA2fQ.ckwjRsdNu9UUKUtdO3G32SwUUoMj7FAAOuBqVsIemo0'; + const updateCredentialsMock = partialSpyOn(obtainToken, 'updateCredentials'); + const refreshMock = partialSpyOn(driveServerWip.auth, 'refresh'); beforeEach(() => { vi.useFakeTimers(); @@ -29,7 +29,7 @@ describe('Token Scheduler', () => { it('should not schedule if token does not have expiration time', () => { // Given - obtainTokenMock.mockReturnValue(jwtWithoutExpiration); + obtainTokenMock.mockReturnValue(createToken('0 day')); // When TokenScheduler.schedule(); // Then @@ -47,20 +47,35 @@ describe('Token Scheduler', () => { TokenScheduler.schedule(); // Then expect(TokenScheduler.getTimeout()).toBe(undefined); - call(loggerMock.error).toMatchObject({ msg: 'Error scheduling refresh token' }); + call(loggerMock.error).toMatchObject({ + msg: 'Error scheduling refresh token', + error: expect.objectContaining({ + message: expect.stringContaining('Invalid token specified'), + }), + }); }); - it('should schedule if token is valid', () => { + it('should refresh if token is expired', async () => { // Given - obtainTokenMock.mockReturnValue(createToken('31 day')); + obtainTokenMock.mockReturnValueOnce(createToken('1 day')).mockReturnValueOnce(createToken('31 day')); + refreshMock.mockResolvedValue({ data: { newToken: 'token' } }); // When TokenScheduler.schedule(); + await vi.runOnlyPendingTimersAsync(); // Then expect(TokenScheduler.getTimeout()).not.toBe(undefined); - call(loggerMock.debug).toMatchObject({ - msg: 'Token renew date', - expiresAt: new Date('1970-02-01'), - renewAt: new Date('1970-01-31'), - }); + call(updateCredentialsMock).toStrictEqual({ newToken: 'token' }); + calls(loggerMock.debug).toMatchObject([ + { + msg: 'Token renew date', + expiresAt: new Date('1970-01-02'), + renewAt: new Date('1970-01-01'), + }, + { + msg: 'Token renew date', + expiresAt: new Date('1970-02-01'), + renewAt: new Date('1970-01-31'), + }, + ]); }); }); From 2444568446656b7ca14c62d926ca3354ffdb02b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jim=C3=A9nez=20Rivera?= Date: Wed, 11 Feb 2026 17:12:44 +0100 Subject: [PATCH 7/8] Commit --- src/apps/main/auth/handlers.test.ts | 15 ++++- src/apps/main/auth/handlers.ts | 4 +- .../main/token-scheduler/TokenScheduler.ts | 61 ++++++++----------- .../token-scheduler/token-scheduler.test.ts | 18 +++--- 4 files changed, 50 insertions(+), 48 deletions(-) diff --git a/src/apps/main/auth/handlers.test.ts b/src/apps/main/auth/handlers.test.ts index 09037672cc..687c7f2c7d 100644 --- a/src/apps/main/auth/handlers.test.ts +++ b/src/apps/main/auth/handlers.test.ts @@ -5,7 +5,7 @@ import { TokenScheduler } from '../token-scheduler/TokenScheduler'; describe('handlers', () => { const getUserMock = partialSpyOn(getUser, 'getUser'); - const getRenewAtMock = partialSpyOn(TokenScheduler, 'getRenewAt'); + const getMillisecondsToRenewMock = partialSpyOn(TokenScheduler, 'getMillisecondsToRenew'); describe('checkUserIsLoggedIn', () => { beforeEach(() => { @@ -32,7 +32,16 @@ describe('handlers', () => { it('should return false if token is expired', () => { // Given - getRenewAtMock.mockReturnValue({ msToRenew: -1 }); + getMillisecondsToRenewMock.mockReturnValue(-1); + // When + const res = checkIfUserIsLoggedIn(); + // Then + expect(res).toBe(false); + }); + + it('should return false if cannot get token', () => { + // Given + getMillisecondsToRenewMock.mockReturnValue(null); // When const res = checkIfUserIsLoggedIn(); // Then @@ -41,7 +50,7 @@ describe('handlers', () => { it('should return true if token is not expired', () => { // Given - getRenewAtMock.mockReturnValue({ msToRenew: 100 }); + getMillisecondsToRenewMock.mockReturnValue(100); // When const res = checkIfUserIsLoggedIn(); // Then diff --git a/src/apps/main/auth/handlers.ts b/src/apps/main/auth/handlers.ts index d44d32e795..c38e2d6e89 100644 --- a/src/apps/main/auth/handlers.ts +++ b/src/apps/main/auth/handlers.ts @@ -44,8 +44,8 @@ export function checkIfUserIsLoggedIn() { return false; } - const { msToRenew } = TokenScheduler.getRenewAt(); - if (msToRenew <= 0) { + const msToRenew = TokenScheduler.getMillisecondsToRenew(); + if (msToRenew === null || msToRenew <= 0) { logger.debug({ tag: 'AUTH', msg: 'User token is expired' }); return false; } diff --git a/src/apps/main/token-scheduler/TokenScheduler.ts b/src/apps/main/token-scheduler/TokenScheduler.ts index 0877a320e0..0d2dac4660 100644 --- a/src/apps/main/token-scheduler/TokenScheduler.ts +++ b/src/apps/main/token-scheduler/TokenScheduler.ts @@ -6,54 +6,47 @@ import { driveServerWip } from '@/infra/drive-server-wip/drive-server-wip.module const DAYS_BEFORE = 1; export class TokenScheduler { - private static timeout: NodeJS.Timeout | undefined; + static timeout: NodeJS.Timeout | undefined; - static getTimeout() { - return this.timeout; - } - - static getExpiresAt() { - const token = obtainToken(); - const decoded = jwtDecode(token); - if (!decoded.exp) throw new Error('Token does not have expiration time'); - return decoded.exp * 1000; - } - - static getRenewAt() { - const expiresAt = this.getExpiresAt(); - const renewAt = expiresAt - DAYS_BEFORE * 24 * 60 * 60 * 1000; - const msToRenew = renewAt - Date.now(); - return { expiresAt, renewAt, msToRenew }; - } - - static schedule() { + static getMillisecondsToRenew() { try { - const { expiresAt, renewAt, msToRenew } = this.getRenewAt(); + const token = obtainToken(); + const decoded = jwtDecode(token); + if (!decoded.exp) throw new Error('Token does not have expiration time'); + + const expiresAt = decoded.exp * 1000; + const renewAt = expiresAt - DAYS_BEFORE * 24 * 60 * 60 * 1000; + const msToRenew = renewAt - Date.now(); logger.debug({ tag: 'AUTH', msg: 'Token renew date', expiresAt: new Date(expiresAt), renewAt: new Date(renewAt), + msToRenew, }); - this.timeout = setTimeout(async () => { - const { data } = await driveServerWip.auth.refresh(); - - if (data) { - updateCredentials({ newToken: data.newToken }); - this.schedule(); - } - }, msToRenew); + return msToRenew; } catch (error) { - logger.error({ - tag: 'AUTH', - msg: 'Error scheduling refresh token', - error, - }); + logger.error({ tag: 'AUTH', msg: 'Error getting token', error }); + return null; } } + static schedule() { + const msToRenew = this.getMillisecondsToRenew(); + if (msToRenew === null) return; + + this.timeout = setTimeout(async () => { + const { data } = await driveServerWip.auth.refresh(); + + if (data) { + updateCredentials({ newToken: data.newToken }); + this.schedule(); + } + }, msToRenew); + } + static stop() { clearTimeout(this.timeout); } diff --git a/src/apps/main/token-scheduler/token-scheduler.test.ts b/src/apps/main/token-scheduler/token-scheduler.test.ts index 8bf410c402..6587598fe5 100644 --- a/src/apps/main/token-scheduler/token-scheduler.test.ts +++ b/src/apps/main/token-scheduler/token-scheduler.test.ts @@ -27,28 +27,28 @@ describe('token-scheduler', () => { vi.useRealTimers(); }); - it('should not schedule if token does not have expiration time', () => { + it('should return Infinity if token does not have expiration time', () => { // Given obtainTokenMock.mockReturnValue(createToken('0 day')); // When - TokenScheduler.schedule(); + TokenScheduler.getMillisecondsToRenew(); // Then - expect(TokenScheduler.getTimeout()).toBe(undefined); + expect(TokenScheduler.timeout).toBeUndefined(); call(loggerMock.error).toMatchObject({ error: new Error('Token does not have expiration time'), - msg: 'Error scheduling refresh token', + msg: 'Error getting token', }); }); - it('should not schedule if token is invalid', () => { + it('should return Infinity if token is invalid', () => { // Given obtainTokenMock.mockReturnValue('invalid'); // When - TokenScheduler.schedule(); + TokenScheduler.getMillisecondsToRenew(); // Then - expect(TokenScheduler.getTimeout()).toBe(undefined); + expect(TokenScheduler.timeout).toBeUndefined(); call(loggerMock.error).toMatchObject({ - msg: 'Error scheduling refresh token', + msg: 'Error getting token', error: expect.objectContaining({ message: expect.stringContaining('Invalid token specified'), }), @@ -63,7 +63,7 @@ describe('token-scheduler', () => { TokenScheduler.schedule(); await vi.runOnlyPendingTimersAsync(); // Then - expect(TokenScheduler.getTimeout()).not.toBe(undefined); + expect(TokenScheduler.timeout).not.toBe(undefined); call(updateCredentialsMock).toStrictEqual({ newToken: 'token' }); calls(loggerMock.debug).toMatchObject([ { From 551d80a67ea3abf5144877fa2a1416d9d5134328 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jim=C3=A9nez=20Rivera?= Date: Wed, 11 Feb 2026 17:12:53 +0100 Subject: [PATCH 8/8] Update .eslintrc.js --- .eslintrc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.eslintrc.js b/.eslintrc.js index f6c832469f..3a2eaa5081 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -67,7 +67,7 @@ module.exports = { 'sonarjs/no-useless-intersection': 'warn', 'sonarjs/prefer-read-only-props': 'off', 'sonarjs/pseudo-random': 'warn', - 'sonarjs/public-static-readonly': 'warn', + 'sonarjs/public-static-readonly': 'off', 'sonarjs/redundant-type-aliases': 'off', 'sonarjs/slow-regex': 'off', 'sonarjs/todo-tag': 'off',