From a787f6ffc79d6234f58868b3815c33b73d7d9952 Mon Sep 17 00:00:00 2001 From: kolaente Date: Fri, 9 Jul 2021 20:10:57 +0200 Subject: [PATCH] Save auth tokens from link shares only in memory, don't persist them to localStorage Resolves #587 --- src/helpers/auth.js | 62 +++++++++++++++++++++++++++++++++ src/registerServiceWorker.js | 3 +- src/services/abstractService.js | 10 +++--- src/store/modules/auth.js | 28 ++++++--------- 4 files changed, 78 insertions(+), 25 deletions(-) create mode 100644 src/helpers/auth.js diff --git a/src/helpers/auth.js b/src/helpers/auth.js new file mode 100644 index 00000000..beffc20c --- /dev/null +++ b/src/helpers/auth.js @@ -0,0 +1,62 @@ +import {HTTPFactory} from '@/http-common' + +let savedToken = null +let persisted = false + +/** + * Saves a token while optionally saving it to lacal storage. This is used when viewing a link share: + * It enables viewing multiple link shares indipendently from each in multiple tabs other without overriding any other open ones. + * @param token + * @param persist + */ +export const saveToken = (token, persist = true) => { + savedToken = token + if (persist) { + persisted = true + localStorage.setItem('token', token) + } +} + +/** + * Returns a saved token. If there is one saved in memory it will use that before anything else. + * @returns {string|null} + */ +export const getToken = () => { + if (savedToken !== null) { + return savedToken + } + + savedToken = localStorage.getItem('token') + return savedToken +} + +/** + * Removes all tokens everywhere. + */ +export const removeToken = () => { + savedToken = null + localStorage.removeItem('token') +} + +/** + * Refreshes an auth token while ensuring it is updated everywhere. + * @returns {Promise>} + */ +export const refreshToken = () => { + const HTTP = HTTPFactory() + return HTTP.post('user/token', null, { + headers: { + Authorization: `Bearer ${getToken()}`, + }, + }) + .then(r => { + saveToken(r.data.token, persisted) + return Promise.resolve(r) + }) + .catch(e => { + // eslint-disable-next-line + console.log('Error renewing token: ', e) + return Promise.reject(e) + }) +} + diff --git a/src/registerServiceWorker.js b/src/registerServiceWorker.js index e198e95f..76ff257d 100644 --- a/src/registerServiceWorker.js +++ b/src/registerServiceWorker.js @@ -2,6 +2,7 @@ import {register} from 'register-service-worker' import swEvents from './ServiceWorker/events' +import {getToken} from './helpers/auth' if (process.env.NODE_ENV === 'production') { register(`${process.env.BASE_URL}sw.js`, { @@ -44,7 +45,7 @@ if (navigator && navigator.serviceWorker) { if (action === 'getBearerToken') { console.debug('Token request from sw') port.postMessage({ - authToken: localStorage.getItem('token'), + authToken: getToken(), }) } else { console.error('Unknown event', event) diff --git a/src/services/abstractService.js b/src/services/abstractService.js index e7501af6..86a8abda 100644 --- a/src/services/abstractService.js +++ b/src/services/abstractService.js @@ -2,6 +2,7 @@ import axios from 'axios' import reduce from 'lodash/reduce' import replace from 'lodash/replace' import {objectToSnakeCase} from '@/helpers/case' +import {getToken} from '@/helpers/auth' export default class AbstractService { @@ -66,12 +67,9 @@ export default class AbstractService { }) // Set the default auth header if we have a token - if ( - localStorage.getItem('token') !== '' && - localStorage.getItem('token') !== null && - localStorage.getItem('token') !== undefined - ) { - this.http.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem('token') + const token = getToken() + if (token !== null) { + this.http.defaults.headers.common['Authorization'] = `Bearer ${token}` } this.paths = { diff --git a/src/store/modules/auth.js b/src/store/modules/auth.js index 0e32414d..59ae66da 100644 --- a/src/store/modules/auth.js +++ b/src/store/modules/auth.js @@ -1,6 +1,7 @@ import {HTTPFactory} from '@/http-common' import {ERROR_MESSAGE, LOADING} from '../mutation-types' import UserModel from '../../models/user' +import {getToken, refreshToken, removeToken, saveToken} from '@/helpers/auth' const defaultSettings = settings => { if (typeof settings.weekStart === 'undefined' || settings.weekStart === '') { @@ -60,7 +61,7 @@ export default { ctx.commit(LOADING, true, {root: true}) // Delete an eventually preexisting old token - localStorage.removeItem('token') + removeToken() const data = { username: credentials.username, @@ -74,7 +75,7 @@ export default { return HTTP.post('login', data) .then(response => { // Save the token to local storage for later use - localStorage.setItem('token', response.data.token) + saveToken(response.data.token) // Tell others the user is autheticated ctx.commit('isLinkShareAuth', false) @@ -127,11 +128,11 @@ export default { } // Delete an eventually preexisting old token - localStorage.removeItem('token') + removeToken() return HTTP.post(`/auth/openid/${provider}/callback`, data) .then(response => { // Save the token to local storage for later use - localStorage.setItem('token', response.data.token) + saveToken(response.data.token) // Tell others the user is autheticated ctx.commit('isLinkShareAuth', false) @@ -151,7 +152,7 @@ export default { password: password, }) .then(r => { - localStorage.setItem('token', r.data.token) + saveToken(r.data.token, false) ctx.dispatch('checkAuth') return Promise.resolve(r.data) }).catch(e => { @@ -167,7 +168,7 @@ export default { return Promise.resolve() } - const jwt = localStorage.getItem('token') + const jwt = getToken() let authenticated = false if (jwt) { const base64 = jwt @@ -213,24 +214,15 @@ export default { }, // Renews the api token and saves it to local storage renewToken(ctx) { - const HTTP = HTTPFactory() if (!ctx.state.authenticated) { return } - HTTP.post('user/token', null, { - headers: { - Authorization: 'Bearer ' + localStorage.getItem('token'), - }, - }) - .then(r => { - localStorage.setItem('token', r.data.token) + refreshToken() + .then(() => { ctx.dispatch('checkAuth') }) .catch(e => { - // eslint-disable-next-line - console.log('Error renewing token: ', e) - // Don't logout on network errors as the user would then get logged out if they don't have // internet for a short period of time - such as when the laptop is still reconnecting if (e.request.status) { @@ -239,7 +231,7 @@ export default { }) }, logout(ctx) { - localStorage.removeItem('token') + removeToken() ctx.dispatch('checkAuth') }, },