Update to use x-refresh-token header, update logout to clear refresh token, add AbsLogger logs for android refresh
Some checks are pending
Build APK / main (push) Waiting to run
Verify all i18n files are alphabetized / update_translations (push) Waiting to run

This commit is contained in:
advplyr 2025-07-04 17:41:19 -05:00
parent d8cdb7073e
commit 467fedbfe7
10 changed files with 123 additions and 90 deletions

View file

@ -216,6 +216,16 @@ class AbsDatabase : Plugin() {
} }
} }
@PluginMethod
fun clearRefreshToken(call:PluginCall) {
val serverConnectionConfigId = call.getString("serverConnectionConfigId", "").toString()
val refreshToken = secureStorage.removeRefreshToken(serverConnectionConfigId)
val result = JSObject()
result.put("success", refreshToken)
call.resolve(result)
}
@PluginMethod @PluginMethod
fun getAccessToken(call:PluginCall) { fun getAccessToken(call:PluginCall) {
val serverConnectionConfigId = call.getString("serverConnectionConfigId", "").toString() val serverConnectionConfigId = call.getString("serverConnectionConfigId", "").toString()

View file

@ -123,7 +123,7 @@ class ApiHandler(var ctx:Context) {
response.use { response.use {
if (it.code == 401) { if (it.code == 401) {
// Handle 401 Unauthorized by attempting token refresh // Handle 401 Unauthorized by attempting token refresh
Log.d(tag, "Received 401, attempting token refresh") AbsLogger.info(tag, "makeRequest: 401 Unauthorized for request to \"${request.url}\" - attempt token refresh")
handleTokenRefresh(request, httpClient, cb) handleTokenRefresh(request, httpClient, cb)
return return
} }
@ -175,12 +175,12 @@ class ApiHandler(var ctx:Context) {
*/ */
private fun handleTokenRefresh(originalRequest: Request, httpClient: OkHttpClient?, callback: (JSObject) -> Unit) { private fun handleTokenRefresh(originalRequest: Request, httpClient: OkHttpClient?, callback: (JSObject) -> Unit) {
try { try {
Log.d(tag, "handleTokenRefresh: Starting token refresh process") AbsLogger.info(tag, "handleTokenRefresh: Attempting to refresh auth tokens for server ${DeviceManager.serverConnectionConfigString}")
// Get current server connection config ID // Get current server connection config ID
val serverConnectionConfigId = DeviceManager.serverConnectionConfigId val serverConnectionConfigId = DeviceManager.serverConnectionConfigId
if (serverConnectionConfigId.isEmpty()) { if (serverConnectionConfigId.isEmpty()) {
Log.e(tag, "handleTokenRefresh: No server connection config ID available") AbsLogger.error(tag, "handleTokenRefresh: Unable to refresh auth tokens. No server connection config ID")
val errorObj = JSObject() val errorObj = JSObject()
errorObj.put("error", "No server connection available") errorObj.put("error", "No server connection available")
callback(errorObj) callback(errorObj)
@ -190,7 +190,7 @@ class ApiHandler(var ctx:Context) {
// Get refresh token from secure storage // Get refresh token from secure storage
val refreshToken = secureStorage.getRefreshToken(serverConnectionConfigId) val refreshToken = secureStorage.getRefreshToken(serverConnectionConfigId)
if (refreshToken.isNullOrEmpty()) { if (refreshToken.isNullOrEmpty()) {
Log.e(tag, "handleTokenRefresh: No refresh token available for server $serverConnectionConfigId") AbsLogger.error(tag, "handleTokenRefresh: Unable to refresh auth tokens. No refresh token available for server ${DeviceManager.serverConnectionConfigString}")
val errorObj = JSObject() val errorObj = JSObject()
errorObj.put("error", "No refresh token available") errorObj.put("error", "No refresh token available")
callback(errorObj) callback(errorObj)
@ -203,7 +203,7 @@ class ApiHandler(var ctx:Context) {
val refreshEndpoint = "${DeviceManager.serverAddress}/auth/refresh" val refreshEndpoint = "${DeviceManager.serverAddress}/auth/refresh"
val refreshRequest = Request.Builder() val refreshRequest = Request.Builder()
.url(refreshEndpoint) .url(refreshEndpoint)
.addHeader("Authorization", "Bearer $refreshToken") .addHeader("x-refresh-token", refreshToken)
.addHeader("Content-Type", "application/json") .addHeader("Content-Type", "application/json")
.post(EMPTY_REQUEST) .post(EMPTY_REQUEST)
.build() .build()
@ -213,13 +213,14 @@ class ApiHandler(var ctx:Context) {
client.newCall(refreshRequest).enqueue(object : Callback { client.newCall(refreshRequest).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) { override fun onFailure(call: Call, e: IOException) {
Log.e(tag, "handleTokenRefresh: Failed to connect to refresh endpoint", e) Log.e(tag, "handleTokenRefresh: Failed to connect to refresh endpoint", e)
AbsLogger.error(tag, "handleTokenRefresh: Failed to connect to refresh endpoint for server ${DeviceManager.serverConnectionConfigString} (error: ${e.message})")
handleRefreshFailure(callback) handleRefreshFailure(callback)
} }
override fun onResponse(call: Call, response: Response) { override fun onResponse(call: Call, response: Response) {
response.use { response.use {
if (!it.isSuccessful) { if (!it.isSuccessful) {
Log.e(tag, "handleTokenRefresh: Refresh request failed with status ${it.code}") AbsLogger.error(tag, "handleTokenRefresh: Refresh request failed with status ${it.code} for server ${DeviceManager.serverConnectionConfigString}")
handleRefreshFailure(callback) handleRefreshFailure(callback)
return return
} }
@ -230,7 +231,7 @@ class ApiHandler(var ctx:Context) {
val userObj = responseJson.optJSONObject("user") val userObj = responseJson.optJSONObject("user")
if (userObj == null) { if (userObj == null) {
Log.e(tag, "handleTokenRefresh: No user object in refresh response") AbsLogger.error(tag, "handleTokenRefresh: No user object in refresh response for server ${DeviceManager.serverConnectionConfigString}")
handleRefreshFailure(callback) handleRefreshFailure(callback)
return return
} }
@ -239,7 +240,7 @@ class ApiHandler(var ctx:Context) {
val newRefreshToken = userObj.optString("refreshToken") val newRefreshToken = userObj.optString("refreshToken")
if (newAccessToken.isEmpty()) { if (newAccessToken.isEmpty()) {
Log.e(tag, "handleTokenRefresh: No access token in refresh response") AbsLogger.error(tag, "handleTokenRefresh: No access token in refresh response for server ${DeviceManager.serverConnectionConfigString}")
handleRefreshFailure(callback) handleRefreshFailure(callback)
return return
} }
@ -255,6 +256,7 @@ class ApiHandler(var ctx:Context) {
} catch (e: Exception) { } catch (e: Exception) {
Log.e(tag, "handleTokenRefresh: Failed to parse refresh response", e) Log.e(tag, "handleTokenRefresh: Failed to parse refresh response", e)
AbsLogger.error(tag, "handleTokenRefresh: Failed to parse refresh response for server ${DeviceManager.serverConnectionConfigString} (error: ${e.message})")
handleRefreshFailure(callback) handleRefreshFailure(callback)
} }
} }
@ -297,8 +299,10 @@ class ApiHandler(var ctx:Context) {
// Can happen if Webview is never run // Can happen if Webview is never run
Log.i(tag, "AbsDatabaseNotifyListeners is not initialized so cannot send new access token") Log.i(tag, "AbsDatabaseNotifyListeners is not initialized so cannot send new access token")
} }
AbsLogger.info(tag, "updateTokens: Successfully refreshed auth tokens for server ${DeviceManager.serverConnectionConfigString}")
} catch (e: Exception) { } catch (e: Exception) {
Log.e(tag, "updateTokens: Failed to update tokens", e) Log.e(tag, "updateTokens: Failed to update tokens", e)
AbsLogger.error(tag, "updateTokens: Failed to refresh auth tokens for server ${DeviceManager.serverConnectionConfigString} (error: ${e.message})")
} }
} }
@ -325,6 +329,7 @@ class ApiHandler(var ctx:Context) {
client.newCall(newRequest).enqueue(object : Callback { client.newCall(newRequest).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) { override fun onFailure(call: Call, e: IOException) {
Log.e(tag, "retryOriginalRequest: Failed to retry request", e) Log.e(tag, "retryOriginalRequest: Failed to retry request", e)
AbsLogger.error(tag, "retryOriginalRequest: Failed to retry request after token refresh for server ${DeviceManager.serverConnectionConfigString} (error: ${e.message})")
val errorObj = JSObject() val errorObj = JSObject()
errorObj.put("error", "Failed to retry request after token refresh") errorObj.put("error", "Failed to retry request after token refresh")
callback(errorObj) callback(errorObj)
@ -334,6 +339,7 @@ class ApiHandler(var ctx:Context) {
response.use { response.use {
if (!it.isSuccessful) { if (!it.isSuccessful) {
Log.e(tag, "retryOriginalRequest: Retry request failed with status ${it.code}") Log.e(tag, "retryOriginalRequest: Retry request failed with status ${it.code}")
AbsLogger.error(tag, "retryOriginalRequest: Retry request failed with status ${it.code} for server ${DeviceManager.serverConnectionConfigString}")
val errorObj = JSObject() val errorObj = JSObject()
errorObj.put("error", "Retry request failed with status ${it.code}") errorObj.put("error", "Retry request failed with status ${it.code}")
callback(errorObj) callback(errorObj)
@ -366,6 +372,7 @@ class ApiHandler(var ctx:Context) {
} catch (e: Exception) { } catch (e: Exception) {
Log.e(tag, "retryOriginalRequest: Unexpected error during retry", e) Log.e(tag, "retryOriginalRequest: Unexpected error during retry", e)
AbsLogger.error(tag, "retryOriginalRequest: Unexpected error during retry for server ${DeviceManager.serverConnectionConfigString}")
val errorObj = JSObject() val errorObj = JSObject()
errorObj.put("error", "Failed to retry request") errorObj.put("error", "Failed to retry request")
callback(errorObj) callback(errorObj)
@ -389,7 +396,7 @@ class ApiHandler(var ctx:Context) {
// Remove refresh token from secure storage // Remove refresh token from secure storage
val serverConnectionConfigId = DeviceManager.serverConnectionConfigId val serverConnectionConfigId = DeviceManager.serverConnectionConfigId
if (!serverConnectionConfigId.isNullOrEmpty()) { if (serverConnectionConfigId.isNotEmpty()) {
secureStorage.removeRefreshToken(serverConnectionConfigId) secureStorage.removeRefreshToken(serverConnectionConfigId)
} }

View file

@ -179,21 +179,7 @@ export default {
this.show = false this.show = false
}, },
async logout() { async logout() {
if (this.user) { await this.$store.dispatch('user/logout', {})
if (this.$store.getters['getIsPlayerOpen']) {
this.$eventBus.$emit('close-stream')
}
await this.$nativeHttp.post('/logout').catch((error) => {
console.error('Failed to logout', error)
})
}
this.$socket.logout()
await this.$db.logout()
this.$localStore.removeLastLibraryId()
this.$store.commit('user/logout')
this.$store.commit('libraries/setCurrentLibrary', null)
}, },
async disconnect() { async disconnect() {
await this.$hapticsImpact() await this.$hapticsImpact()

View file

@ -260,6 +260,7 @@ export default {
return null return null
}, },
ebookFile() { ebookFile() {
if (!this.media) return null
// ebook file id is passed when reading a supplementary ebook // ebook file id is passed when reading a supplementary ebook
if (this.ebookFileId) { if (this.ebookFileId) {
return this.selectedLibraryItem.libraryFiles.find((lf) => lf.ino === this.ebookFileId) return this.selectedLibraryItem.libraryFiles.find((lf) => lf.ino === this.ebookFileId)

View file

@ -48,17 +48,7 @@ export default {
methods: { methods: {
async logout() { async logout() {
await this.$hapticsImpact() await this.$hapticsImpact()
if (this.user) { await this.$store.dispatch('user/logout', {})
await this.$nativeHttp.post('/logout').catch((error) => {
console.error(error)
})
}
this.$socket.logout()
await this.$db.logout()
this.$localStore.removeLastLibraryId()
this.$store.commit('user/logout')
this.$store.commit('libraries/setCurrentLibrary', null)
this.$router.push('/connect') this.$router.push('/connect')
} }
}, },

View file

@ -70,7 +70,12 @@ class AbsAudioPlayerWeb extends WebPlugin {
const deviceInfo = { const deviceInfo = {
deviceId: this.getDeviceId() deviceId: this.getDeviceId()
} }
const playbackSession = await $axios.$post(route, { deviceInfo, mediaPlayer: 'html5-mobile', forceDirectPlay: true }) const reqBody = {
deviceInfo,
mediaPlayer: 'html5-mobile',
forceDirectPlay: true
}
const playbackSession = await $axios.$post(route, reqBody)
if (playbackSession) { if (playbackSession) {
if (startTime !== undefined && startTime !== null) playbackSession.currentTime = startTime if (startTime !== undefined && startTime !== null) playbackSession.currentTime = startTime
this.setAudioPlayer(playbackSession, playWhenReady) this.setAudioPlayer(playbackSession, playWhenReady)
@ -245,7 +250,15 @@ class AbsAudioPlayerWeb extends WebPlugin {
this.trackStartTime = Math.max(0, this.startTime - (this.currentTrack.startOffset || 0)) this.trackStartTime = Math.max(0, this.startTime - (this.currentTrack.startOffset || 0))
const serverAddressUrl = new URL(vuexStore.getters['user/getServerAddress']) const serverAddressUrl = new URL(vuexStore.getters['user/getServerAddress'])
const serverHost = `${serverAddressUrl.protocol}//${serverAddressUrl.host}` const serverHost = `${serverAddressUrl.protocol}//${serverAddressUrl.host}`
this.player.src = `${serverHost}${this.currentTrack.contentUrl}`
let sessionTrackUrl = null
if (this.currentTrack.contentUrl?.startsWith('/hls')) {
sessionTrackUrl = this.currentTrack.contentUrl
} else {
sessionTrackUrl = `/public/session/${this.playbackSession.id}/track/${this.currentTrack.index}`
}
this.player.src = `${serverHost}${sessionTrackUrl}`
console.log(`[AbsAudioPlayer] Loading track src ${this.player.src}`) console.log(`[AbsAudioPlayer] Loading track src ${this.player.src}`)
this.player.load() this.player.load()
this.player.playbackRate = this.playbackRate this.player.playbackRate = this.playbackRate

View file

@ -69,6 +69,11 @@ class AbsDatabaseWeb extends WebPlugin {
return refreshToken ? { refreshToken } : null return refreshToken ? { refreshToken } : null
} }
async clearRefreshToken({ serverConnectionConfigId }) {
console.log('[AbsDatabase] Clearing refresh token...', serverConnectionConfigId)
localStorage.removeItem(`refresh_token_${serverConnectionConfigId}`)
}
async removeServerConnectionConfig(serverConnectionConfigCallObject) { async removeServerConnectionConfig(serverConnectionConfigCallObject) {
var serverConnectionConfigId = serverConnectionConfigCallObject.serverConnectionConfigId var serverConnectionConfigId = serverConnectionConfigCallObject.serverConnectionConfigId
var deviceData = await this.getDeviceData() var deviceData = await this.getDeviceData()
@ -77,6 +82,7 @@ class AbsDatabaseWeb extends WebPlugin {
} }
async logout() { async logout() {
console.log('[AbsDatabase] Logging out...')
var deviceData = await this.getDeviceData() var deviceData = await this.getDeviceData()
deviceData.lastServerConnectionConfigId = null deviceData.lastServerConnectionConfigId = null
localStorage.setItem('device', JSON.stringify(deviceData)) localStorage.setItem('device', JSON.stringify(deviceData))

View file

@ -10,6 +10,26 @@ class DbService {
}) })
} }
/**
* Retrieves refresh token from secure storage
* @param {string} serverConnectionConfigId
* @return {Promise<string|null>}
*/
async getRefreshToken(serverConnectionConfigId) {
const refreshTokenData = await AbsDatabase.getRefreshToken({ serverConnectionConfigId })
return refreshTokenData?.refreshToken
}
/**
* Clears refresh token from secure storage
* @param {string} serverConnectionConfigId
* @returns {Promise<boolean>}
*/
async clearRefreshToken(serverConnectionConfigId) {
const result = await AbsDatabase.clearRefreshToken({ serverConnectionConfigId })
return !!result?.success
}
setServerConnectionConfig(serverConnectionConfig) { setServerConnectionConfig(serverConnectionConfig) {
return AbsDatabase.setCurrentServerConnectionConfig(serverConnectionConfig).then((data) => { return AbsDatabase.setCurrentServerConnectionConfig(serverConnectionConfig).then((data) => {
console.log('Set server connection config', JSON.stringify(data)) console.log('Set server connection config', JSON.stringify(data))

View file

@ -1,7 +1,6 @@
import { CapacitorHttp } from '@capacitor/core' import { CapacitorHttp } from '@capacitor/core'
import { AbsDatabase } from '@/plugins/capacitor'
export default function ({ store }, inject) { export default function ({ store, $db }, inject) {
const nativeHttp = { const nativeHttp = {
async request(method, _url, data, options = {}) { async request(method, _url, data, options = {}) {
// When authorizing before a config is set, server config gets passed in as an option // When authorizing before a config is set, server config gets passed in as an option
@ -9,7 +8,7 @@ export default function ({ store }, inject) {
delete options.serverConnectionConfig delete options.serverConnectionConfig
let url = _url let url = _url
const headers = {} let headers = {}
if (!url.startsWith('http') && !url.startsWith('capacitor')) { if (!url.startsWith('http') && !url.startsWith('capacitor')) {
const bearerToken = store.getters['user/getToken'] const bearerToken = store.getters['user/getToken']
if (bearerToken) { if (bearerToken) {
@ -24,6 +23,10 @@ export default function ({ store }, inject) {
if (data) { if (data) {
headers['Content-Type'] = 'application/json' headers['Content-Type'] = 'application/json'
} }
if (options.headers) {
headers = { ...headers, ...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,
@ -65,15 +68,15 @@ export default function ({ store }, inject) {
} }
// Get refresh token from secure storage // Get refresh token from secure storage
const refreshTokenData = await this.getRefreshToken(serverConnectionConfig.id) const refreshToken = await $db.getRefreshToken(serverConnectionConfig.id)
if (!refreshTokenData || !refreshTokenData.refreshToken) { if (!refreshToken) {
console.error('[nativeHttp] No refresh token available') console.error('[nativeHttp] No refresh token available')
throw new Error('No refresh token available') throw new Error('No refresh token available')
} }
// Attempt to refresh the token // Attempt to refresh the token
const newTokens = await this.refreshAccessToken(refreshTokenData.refreshToken, serverConnectionConfig.address) const newTokens = await this.refreshAccessToken(refreshToken, serverConnectionConfig.address)
if (!newTokens || !newTokens.accessToken) { if (!newTokens?.accessToken) {
console.error('[nativeHttp] Failed to refresh access token') console.error('[nativeHttp] Failed to refresh access token')
throw new Error('Failed to refresh access token') throw new Error('Failed to refresh access token')
} }
@ -83,15 +86,15 @@ export default function ({ store }, inject) {
// Retry the original request with the new token // Retry the original request with the new token
console.log('[nativeHttp] Retrying original request with new token...') console.log('[nativeHttp] Retrying original request with new token...')
const newHeaders = options?.headers ? { ...options.headers } : { ...headers }
newHeaders['Authorization'] = `Bearer ${newTokens.accessToken}`
const retryResponse = await CapacitorHttp.request({ const retryResponse = await CapacitorHttp.request({
method, method,
url, url,
data, data,
...options, headers: {
headers: newHeaders ...headers,
Authorization: `Bearer ${newTokens.accessToken}`
},
...options
}) })
if (retryResponse.status >= 400) { if (retryResponse.status >= 400) {
@ -104,26 +107,11 @@ export default function ({ store }, inject) {
console.error('[nativeHttp] Token refresh failed:', error) console.error('[nativeHttp] Token refresh failed:', error)
// If refresh fails, redirect to login // If refresh fails, redirect to login
await this.handleRefreshFailure() await this.handleRefreshFailure(serverConnectionConfig?.id)
throw error throw error
} }
}, },
/**
* Retrieves refresh token from secure storage
* @param {string} serverConnectionConfigId - Server connection config ID
* @returns {Promise<Object|null>} - Promise that resolves with refresh token data or null
*/
async getRefreshToken(serverConnectionConfigId) {
try {
console.log('[nativeHttp] Getting refresh token...')
return await AbsDatabase.getRefreshToken({ serverConnectionConfigId })
} catch (error) {
console.error('[nativeHttp] Failed to get refresh token:', error)
return null
}
},
/** /**
* Refreshes the access token using the refresh token * Refreshes the access token using the refresh token
* @param {string} refreshToken - The refresh token * @param {string} refreshToken - The refresh token
@ -142,8 +130,7 @@ export default function ({ store }, inject) {
url: `${serverAddress}/auth/refresh`, url: `${serverAddress}/auth/refresh`,
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
Authorization: `Bearer ${refreshToken}`, 'x-refresh-token': refreshToken
'X-Return-Tokens': 'true'
}, },
data: {} data: {}
}) })
@ -162,7 +149,8 @@ export default function ({ store }, inject) {
console.log('[nativeHttp] Successfully refreshed access token') console.log('[nativeHttp] Successfully refreshed access token')
return { return {
accessToken: userResponseData.user.accessToken, accessToken: userResponseData.user.accessToken,
refreshToken: userResponseData.user.refreshToken || refreshToken // Use new refresh token if provided, otherwise keep the old one // Refresh token gets returned when refresh token is sent in x-refresh-token header
refreshToken: userResponseData.user.refreshToken
} }
} catch (error) { } catch (error) {
console.error('[nativeHttp] Failed to refresh access token:', error) console.error('[nativeHttp] Failed to refresh access token:', error)
@ -190,7 +178,7 @@ export default function ({ store }, inject) {
} }
// Save updated config to secure storage // Save updated config to secure storage
const savedConfig = await AbsDatabase.setCurrentServerConnectionConfig(updatedConfig) const savedConfig = await $db.setServerConnectionConfig(updatedConfig)
// Update the store // Update the store
store.commit('user/setAccessToken', tokens.accessToken) store.commit('user/setAccessToken', tokens.accessToken)
@ -208,19 +196,15 @@ export default function ({ store }, inject) {
/** /**
* Handles the case when token refresh fails * Handles the case when token refresh fails
* @param {string} [serverConnectionConfigId]
* @returns {Promise} - Promise that resolves when logout is complete * @returns {Promise} - Promise that resolves when logout is complete
*/ */
async handleRefreshFailure() { async handleRefreshFailure(serverConnectionConfigId) {
try { try {
console.log('[nativeHttp] Handling refresh failure - logging out user') console.log('[nativeHttp] Handling refresh failure - logging out user')
// Clear the store // Logout from server and clear store
store.commit('user/setUser', null) await store.dispatch('user/logout', { serverConnectionConfigId })
store.commit('user/setAccessToken', null)
store.commit('user/setServerConnectionConfig', null)
// Logout from database
await AbsDatabase.logout()
// Redirect to login page // Redirect to login page
if (window.location.pathname !== '/connect') { if (window.location.pathname !== '/connect') {
@ -231,19 +215,6 @@ export default function ({ store }, inject) {
} }
}, },
/**
* Gets device data from the database
* @returns {Promise<Object>} - Promise that resolves with device data
*/
async getDeviceData() {
try {
return await AbsDatabase.getDeviceData()
} catch (error) {
console.error('[nativeHttp] Failed to get device data:', error)
return { serverConnectionConfigs: [] }
}
},
get(url, options = {}) { get(url, options = {}) {
return this.request('GET', url, undefined, options) return this.request('GET', url, undefined, options)
}, },

View file

@ -1,4 +1,5 @@
import { Browser } from '@capacitor/browser' import { Browser } from '@capacitor/browser'
import { AbsLogger } from '@/plugins/capacitor'
export const state = () => ({ export const state = () => ({
user: null, user: null,
@ -135,12 +136,40 @@ export const actions = {
} catch (error) { } catch (error) {
console.error('Error opening browser', error) console.error('Error opening browser', error)
} }
},
async logout({ state, commit }, { serverConnectionConfigId }) {
if (state.serverConnectionConfig) {
const refreshToken = await this.$db.getRefreshToken(state.serverConnectionConfig.id)
const options = {}
if (refreshToken) {
// Refresh token is used to delete the session on the server
options.headers = {
'x-refresh-token': refreshToken
}
}
// Logout from server
await this.$nativeHttp.post('/logout', null, options).catch((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()
this.$socket.logout()
this.$localStore.removeLastLibraryId()
commit('logout')
commit('libraries/setCurrentLibrary', null, { root: true })
await AbsLogger.info({ tag: 'user', message: `Logged out from server ${state.serverConnectionConfig?.name || 'Not connected'}` })
} }
} }
export const mutations = { export const mutations = {
logout(state) { logout(state) {
state.user = null state.user = null
state.accessToken = null
state.serverConnectionConfig = null state.serverConnectionConfig = null
}, },
setUser(state, user) { setUser(state, user) {