mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2025-08-18 08:38:39 +02:00
Update to not logout from server when switching servers, force users to re-login if using old auth
This commit is contained in:
parent
44613e12f1
commit
f6e2e4010f
7 changed files with 105 additions and 45 deletions
|
@ -179,7 +179,7 @@ export default {
|
||||||
this.show = false
|
this.show = false
|
||||||
},
|
},
|
||||||
async logout() {
|
async logout() {
|
||||||
await this.$store.dispatch('user/logout', {})
|
await this.$store.dispatch('user/logout')
|
||||||
},
|
},
|
||||||
async disconnect() {
|
async disconnect() {
|
||||||
await this.$hapticsImpact()
|
await this.$hapticsImpact()
|
||||||
|
|
|
@ -439,6 +439,7 @@ export default {
|
||||||
const payload = await this.authenticateToken()
|
const payload = await this.authenticateToken()
|
||||||
|
|
||||||
if (payload) {
|
if (payload) {
|
||||||
|
// Will NOT include access token and refresh token
|
||||||
this.setUserAndConnection(payload)
|
this.setUserAndConnection(payload)
|
||||||
} else {
|
} else {
|
||||||
this.showAuth = true
|
this.showAuth = true
|
||||||
|
@ -770,26 +771,6 @@ export default {
|
||||||
prependProtocolIfNeeded(address) {
|
prependProtocolIfNeeded(address) {
|
||||||
return address.startsWith('http://') || address.startsWith('https://') ? address : `https://${address}`
|
return address.startsWith('http://') || address.startsWith('https://') ? address : `https://${address}`
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* Compares two semantic versioning strings to determine if the current version meets
|
|
||||||
* or exceeds the minimum version requirement.
|
|
||||||
*
|
|
||||||
* @param {string} currentVersion - The current version string to compare, e.g., "1.2.3".
|
|
||||||
* @param {string} minVersion - The minimum version string required, e.g., "1.0.0".
|
|
||||||
* @returns {boolean} - Returns true if the current version is greater than or equal
|
|
||||||
* to the minimum version, false otherwise.
|
|
||||||
*/
|
|
||||||
isValidVersion(currentVersion, minVersion) {
|
|
||||||
const currentParts = currentVersion.split('.').map(Number)
|
|
||||||
const minParts = minVersion.split('.').map(Number)
|
|
||||||
|
|
||||||
for (let i = 0; i < minParts.length; i++) {
|
|
||||||
if (currentParts[i] > minParts[i]) return true
|
|
||||||
if (currentParts[i] < minParts[i]) return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
async submitAuth() {
|
async submitAuth() {
|
||||||
if (!this.networkConnected) return
|
if (!this.networkConnected) return
|
||||||
if (!this.serverConfig.username) {
|
if (!this.serverConfig.username) {
|
||||||
|
@ -809,6 +790,7 @@ export default {
|
||||||
const payload = await this.requestServerLogin()
|
const payload = await this.requestServerLogin()
|
||||||
this.processing = false
|
this.processing = false
|
||||||
if (payload) {
|
if (payload) {
|
||||||
|
// Will include access token and refresh token
|
||||||
this.setUserAndConnection(payload)
|
this.setUserAndConnection(payload)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -821,20 +803,27 @@ export default {
|
||||||
this.$store.commit('libraries/setEReaderDevices', ereaderDevices)
|
this.$store.commit('libraries/setEReaderDevices', ereaderDevices)
|
||||||
this.$setServerLanguageCode(serverSettings.language)
|
this.$setServerLanguageCode(serverSettings.language)
|
||||||
|
|
||||||
// Set library - Use last library if set and available fallback to default user library
|
|
||||||
var lastLibraryId = await this.$localStore.getLastLibraryId()
|
|
||||||
if (lastLibraryId && (!user.librariesAccessible.length || user.librariesAccessible.includes(lastLibraryId))) {
|
|
||||||
this.$store.commit('libraries/setCurrentLibrary', lastLibraryId)
|
|
||||||
} else if (userDefaultLibraryId) {
|
|
||||||
this.$store.commit('libraries/setCurrentLibrary', userDefaultLibraryId)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.serverConfig.userId = user.id
|
this.serverConfig.userId = user.id
|
||||||
this.serverConfig.username = user.username
|
this.serverConfig.username = user.username
|
||||||
// Tokens only returned from /login endpoint
|
|
||||||
if (user.accessToken) {
|
if (this.$isValidVersion(serverSettings.version, '2.26.0')) {
|
||||||
this.serverConfig.token = user.accessToken
|
// Tokens only returned from /login endpoint
|
||||||
this.serverConfig.refreshToken = user.refreshToken
|
if (user.accessToken) {
|
||||||
|
this.serverConfig.token = user.accessToken
|
||||||
|
this.serverConfig.refreshToken = user.refreshToken
|
||||||
|
} else {
|
||||||
|
// Detect if the connection config is using the old token. If so, force re-login
|
||||||
|
if (this.serverConfig.token === user.token) {
|
||||||
|
this.setForceReloginForNewAuth()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the token was updated during a refresh (in nativeHttp.js) it gets updated in the store, so refetch
|
||||||
|
this.serverConfig.token = this.$store.getters['user/getToken'] || this.serverConfig.token
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Server version before new JWT auth, use old user.token
|
||||||
|
this.serverConfig.token = user.token
|
||||||
}
|
}
|
||||||
|
|
||||||
this.serverConfig.version = serverSettings.version
|
this.serverConfig.version = serverSettings.version
|
||||||
|
@ -854,6 +843,14 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set library - Use last library if set and available fallback to default user library
|
||||||
|
const lastLibraryId = await this.$localStore.getLastLibraryId()
|
||||||
|
if (lastLibraryId && (!user.librariesAccessible.length || user.librariesAccessible.includes(lastLibraryId))) {
|
||||||
|
this.$store.commit('libraries/setCurrentLibrary', lastLibraryId)
|
||||||
|
} else if (userDefaultLibraryId) {
|
||||||
|
this.$store.commit('libraries/setCurrentLibrary', userDefaultLibraryId)
|
||||||
|
}
|
||||||
|
|
||||||
this.$store.commit('user/setUser', user)
|
this.$store.commit('user/setUser', user)
|
||||||
this.$store.commit('user/setAccessToken', serverConnectionConfig.token)
|
this.$store.commit('user/setAccessToken', serverConnectionConfig.token)
|
||||||
this.$store.commit('user/setServerConnectionConfig', serverConnectionConfig)
|
this.$store.commit('user/setServerConnectionConfig', serverConnectionConfig)
|
||||||
|
@ -871,8 +868,13 @@ export default {
|
||||||
this.error = null
|
this.error = null
|
||||||
this.processing = true
|
this.processing = true
|
||||||
|
|
||||||
// TODO: Handle refresh token
|
const nativeHttpOptions = {
|
||||||
const authRes = await this.postRequest(`${this.serverConfig.address}/api/authorize`, null, { Authorization: `Bearer ${this.serverConfig.token}` }).catch((error) => {
|
headers: {
|
||||||
|
Authorization: `Bearer ${this.serverConfig.token}`
|
||||||
|
},
|
||||||
|
serverConnectionConfig: this.serverConfig
|
||||||
|
}
|
||||||
|
const authRes = await this.$nativeHttp.post(`${this.serverConfig.address}/api/authorize`, null, nativeHttpOptions).catch((error) => {
|
||||||
console.error('[ServerConnectForm] Server auth failed', error)
|
console.error('[ServerConnectForm] Server auth failed', error)
|
||||||
const errorMsg = error.message || error
|
const errorMsg = error.message || error
|
||||||
this.error = 'Failed to authorize'
|
this.error = 'Failed to authorize'
|
||||||
|
@ -881,13 +883,25 @@ export default {
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log('[ServerConnectForm] authRes=', authRes)
|
console.log('[ServerConnectForm] authRes=', authRes)
|
||||||
|
|
||||||
this.processing = false
|
this.processing = false
|
||||||
return authRes
|
return authRes
|
||||||
},
|
},
|
||||||
|
setForceReloginForNewAuth() {
|
||||||
|
this.error = 'A new authentication system was added in server v2.26.0. Re-login is required for this server connection.'
|
||||||
|
this.showAuth = true
|
||||||
|
},
|
||||||
init() {
|
init() {
|
||||||
|
// Handle force re-login for servers using new JWT auth but still using an old token in the server config
|
||||||
|
if (this.$route.query.error === 'oldAuthToken' && this.$route.query.serverConnectionConfigId) {
|
||||||
|
this.serverConfig = this.serverConnectionConfigs.find((scc) => scc.id === this.$route.query.serverConnectionConfigId)
|
||||||
|
if (this.serverConfig) {
|
||||||
|
this.setForceReloginForNewAuth()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this.lastServerConnectionConfig) {
|
if (this.lastServerConnectionConfig) {
|
||||||
console.log('[ServerConnectForm] init with lastServerConnectionConfig', this.lastServerConnectionConfig)
|
console.log('[ServerConnectForm] init with lastServerConnectionConfig', this.lastServerConnectionConfig)
|
||||||
this.connectToServer(this.lastServerConnectionConfig)
|
this.connectToServer(this.lastServerConnectionConfig)
|
||||||
|
|
|
@ -151,6 +151,22 @@ export default {
|
||||||
this.$store.commit('setServerSettings', serverSettings)
|
this.$store.commit('setServerSettings', serverSettings)
|
||||||
this.$store.commit('libraries/setEReaderDevices', ereaderDevices)
|
this.$store.commit('libraries/setEReaderDevices', ereaderDevices)
|
||||||
|
|
||||||
|
if (this.$isValidVersion(serverSettings.version, '2.26.0')) {
|
||||||
|
// Check if the server is using the new JWT auth and is still using an old token in the server config
|
||||||
|
// If so, redirect to /connect and request to re-login
|
||||||
|
if (serverConfig.token === user.token) {
|
||||||
|
this.attemptingConnection = false
|
||||||
|
AbsLogger.info({ tag: 'default', message: `attemptConnection: Server is using new JWT auth but is still using an old token (server version: ${serverSettings.version}) (${serverConfig.name})` })
|
||||||
|
// Clear last server config
|
||||||
|
await this.$store.dispatch('user/logout')
|
||||||
|
this.$router.push(`/connect?error=oldAuthToken&serverConnectionConfigId=${serverConfig.id}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token may have been refreshed during the authorize call so refetch from store
|
||||||
|
serverConfig.token = this.$store.getters['user/getToken'] || serverConfig.token
|
||||||
|
}
|
||||||
|
|
||||||
// Set library - Use last library if set and available fallback to default user library
|
// Set library - Use last library if set and available fallback to default user library
|
||||||
const lastLibraryId = await this.$localStore.getLastLibraryId()
|
const lastLibraryId = await this.$localStore.getLastLibraryId()
|
||||||
if (lastLibraryId && (!user.librariesAccessible.length || user.librariesAccessible.includes(lastLibraryId))) {
|
if (lastLibraryId && (!user.librariesAccessible.length || user.librariesAccessible.includes(lastLibraryId))) {
|
||||||
|
|
|
@ -48,7 +48,7 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
async logout() {
|
async logout() {
|
||||||
await this.$hapticsImpact()
|
await this.$hapticsImpact()
|
||||||
await this.$store.dispatch('user/logout', {})
|
await this.$store.dispatch('user/logout')
|
||||||
this.$router.push('/connect')
|
this.$router.push('/connect')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -245,10 +245,35 @@ Vue.prototype.$sanitizeSlug = (str) => {
|
||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares two semantic versioning strings to determine if the current version meets
|
||||||
|
* or exceeds the minimum version requirement.
|
||||||
|
* Only supports 3 part versions, e.g. "1.2.3"
|
||||||
|
*
|
||||||
|
* @param {string} currentVersion - The current version string to compare, e.g., "1.2.3".
|
||||||
|
* @param {string} minVersion - The minimum version string required, e.g., "1.0.0".
|
||||||
|
* @returns {boolean} - Returns true if the current version is greater than or equal
|
||||||
|
* to the minimum version, false otherwise.
|
||||||
|
*/
|
||||||
|
function isValidVersion(currentVersion, minVersion) {
|
||||||
|
if (!currentVersion || !minVersion) return false
|
||||||
|
const currentParts = currentVersion.split('.').map(Number)
|
||||||
|
const minParts = minVersion.split('.').map(Number)
|
||||||
|
|
||||||
|
for (let i = 0; i < minParts.length; i++) {
|
||||||
|
if (currentParts[i] > minParts[i]) return true
|
||||||
|
if (currentParts[i] < minParts[i]) return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
export default ({ store, app }, inject) => {
|
export default ({ store, app }, inject) => {
|
||||||
const eventBus = new Vue()
|
const eventBus = new Vue()
|
||||||
inject('eventBus', eventBus)
|
inject('eventBus', eventBus)
|
||||||
|
|
||||||
|
inject('isValidVersion', isValidVersion)
|
||||||
|
|
||||||
// Set theme
|
// Set theme
|
||||||
app.$localStore?.getTheme()?.then((theme) => {
|
app.$localStore?.getTheme()?.then((theme) => {
|
||||||
if (theme) {
|
if (theme) {
|
||||||
|
|
|
@ -28,6 +28,7 @@ export default function ({ store, $db }, inject) {
|
||||||
delete options.headers
|
delete options.headers
|
||||||
}
|
}
|
||||||
console.log(`[nativeHttp] Making ${method} request to ${url}`)
|
console.log(`[nativeHttp] Making ${method} request to ${url}`)
|
||||||
|
|
||||||
return CapacitorHttp.request({
|
return CapacitorHttp.request({
|
||||||
method,
|
method,
|
||||||
url,
|
url,
|
||||||
|
@ -177,7 +178,7 @@ export default function ({ store, $db }, inject) {
|
||||||
refreshToken: tokens.refreshToken
|
refreshToken: tokens.refreshToken
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save updated config to secure storage
|
// Save updated config to secure storage, persists refresh token in secure storage
|
||||||
const savedConfig = await $db.setServerConnectionConfig(updatedConfig)
|
const savedConfig = await $db.setServerConnectionConfig(updatedConfig)
|
||||||
|
|
||||||
// Update the store
|
// Update the store
|
||||||
|
@ -204,7 +205,12 @@ export default function ({ store, $db }, inject) {
|
||||||
console.log('[nativeHttp] Handling refresh failure - logging out user')
|
console.log('[nativeHttp] Handling refresh failure - logging out user')
|
||||||
|
|
||||||
// Logout from server and clear store
|
// Logout from server and clear store
|
||||||
await store.dispatch('user/logout', { serverConnectionConfigId })
|
await store.dispatch('user/logout')
|
||||||
|
|
||||||
|
if (serverConnectionConfigId) {
|
||||||
|
// Clear refresh token for server connection config
|
||||||
|
await $db.clearRefreshToken(serverConnectionConfigId)
|
||||||
|
}
|
||||||
|
|
||||||
// Redirect to login page
|
// Redirect to login page
|
||||||
if (window.location.pathname !== '/connect') {
|
if (window.location.pathname !== '/connect') {
|
||||||
|
|
|
@ -140,8 +140,11 @@ export const actions = {
|
||||||
console.error('Error opening browser', error)
|
console.error('Error opening browser', error)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async logout({ state, commit }, { serverConnectionConfigId }) {
|
async logout({ state, commit }, logoutFromServer = false) {
|
||||||
if (state.serverConnectionConfig) {
|
// Logging out from server deletes the session so the refresh token is no longer valid
|
||||||
|
// Currently this is not being used to support switching servers without logging back in (assuming refresh token is still valid)
|
||||||
|
// We may want to make this change in the future
|
||||||
|
if (state.serverConnectionConfig && logoutFromServer) {
|
||||||
const refreshToken = await this.$db.getRefreshToken(state.serverConnectionConfig.id)
|
const refreshToken = await this.$db.getRefreshToken(state.serverConnectionConfig.id)
|
||||||
const options = {}
|
const options = {}
|
||||||
if (refreshToken) {
|
if (refreshToken) {
|
||||||
|
@ -154,10 +157,6 @@ export const actions = {
|
||||||
await this.$nativeHttp.post('/logout', null, options).catch((error) => {
|
await this.$nativeHttp.post('/logout', null, options).catch((error) => {
|
||||||
console.error('Failed to logout', error)
|
console.error('Failed to logout', error)
|
||||||
})
|
})
|
||||||
await this.$db.clearRefreshToken(state.serverConnectionConfig.id)
|
|
||||||
} else if (serverConnectionConfigId) {
|
|
||||||
// When refresh fails before a server connection config is set, clear refresh token for server connection config
|
|
||||||
await this.$db.clearRefreshToken(serverConnectionConfigId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.$db.logout()
|
await this.$db.logout()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue