diff --git a/src/boot/axios.js b/src/boot/axios.js index 4fd83ddea..fa8a08003 100644 --- a/src/boot/axios.js +++ b/src/boot/axios.js @@ -55,10 +55,10 @@ const onResponseError = (error) => { } if (session.isLoggedIn() && response?.status === 401) { - session.destroy(); + session.destroy(false); const hash = window.location.hash; const url = hash.slice(1); - Router.push({ path: url }); + Router.push(`/login?redirect=${url}`); } else if (!session.isLoggedIn()) { return Promise.reject(error); } diff --git a/src/composables/useSession.js b/src/composables/useSession.js index ca2abef00..10791c9c8 100644 --- a/src/composables/useSession.js +++ b/src/composables/useSession.js @@ -58,31 +58,37 @@ export function useSession() { } } } - async function destroy() { + async function destroy(destroyTokens = true) { const tokens = { tokenMultimedia: 'Accounts/logout', token: 'VnUsers/logout', }; const storage = keepLogin() ? localStorage : sessionStorage; + let destroyTokenPromises = []; + try { + if (destroyTokens) { + const { data: isValidToken } = await axios.get('VnUsers/validateToken'); + if (isValidToken) + destroyTokenPromises = Object.entries(tokens).map(([key, url]) => + destroyToken(url, storage, key) + ); + } + } finally { + localStorage.clear(); + sessionStorage.clear(); + await Promise.allSettled(destroyTokenPromises); + const { setUser } = useState(); - for (const [key, url] of Object.entries(tokens)) { - await destroyToken(url, storage, key); + setUser({ + id: 0, + name: '', + nickname: '', + lang: '', + darkMode: null, + }); + + stopRenewer(); } - - localStorage.clear(); - sessionStorage.clear(); - - const { setUser } = useState(); - - setUser({ - id: 0, - name: '', - nickname: '', - lang: '', - darkMode: null, - }); - - stopRenewer(); } async function login(data) { diff --git a/test/cypress/integration/logout.spec.js b/test/cypress/integration/logout.spec.js new file mode 100644 index 000000000..b35a8415a --- /dev/null +++ b/test/cypress/integration/logout.spec.js @@ -0,0 +1,39 @@ +/// +describe('Logout', () => { + beforeEach(() => { + cy.login('developer'); + cy.visit(`/#/dashboard`); + cy.waitForElement('.q-page', 6000); + }); + describe('by user', () => { + it('should logout', () => { + cy.get( + '#user > .q-btn__content > .q-avatar > .q-avatar__content > .q-img > .q-img__container > .q-img__image' + ).click(); + cy.get('.block').click(); + }); + }); + describe('not user', () => { + beforeEach(() => { + cy.intercept('GET', '**/VnUsers/acl', { + statusCode: 401, + body: { + error: { + statusCode: 401, + name: 'Error', + message: 'Authorization Required', + code: 'AUTHORIZATION_REQUIRED', + }, + }, + statusMessage: 'AUTHORIZATION_REQUIRED', + }).as('someRoute'); + }); + it('when token not exists', () => { + cy.reload(); + cy.get('.q-notification__message').should( + 'have.text', + 'Authorization Required' + ); + }); + }); +}); diff --git a/test/vitest/__tests__/composables/useSession.spec.js b/test/vitest/__tests__/composables/useSession.spec.js index 831acbf18..789b149ec 100644 --- a/test/vitest/__tests__/composables/useSession.spec.js +++ b/test/vitest/__tests__/composables/useSession.spec.js @@ -55,6 +55,7 @@ describe('session', () => { expect(user.value).toEqual(previousUser); vi.spyOn(axios, 'post').mockResolvedValue({ data: true }); + vi.spyOn(axios, 'get').mockResolvedValue({ data: true }); await session.destroy(); user = state.getUser();