Update to use x-refresh-token header, update logout to clear refresh token, add AbsLogger logs for android refresh

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
+15 -2
View File
@@ -70,7 +70,12 @@ class AbsAudioPlayerWeb extends WebPlugin {
const deviceInfo = {
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 (startTime !== undefined && startTime !== null) playbackSession.currentTime = startTime
this.setAudioPlayer(playbackSession, playWhenReady)
@@ -245,7 +250,15 @@ class AbsAudioPlayerWeb extends WebPlugin {
this.trackStartTime = Math.max(0, this.startTime - (this.currentTrack.startOffset || 0))
const serverAddressUrl = new URL(vuexStore.getters['user/getServerAddress'])
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}`)
this.player.load()
this.player.playbackRate = this.playbackRate
+6
View File
@@ -69,6 +69,11 @@ class AbsDatabaseWeb extends WebPlugin {
return refreshToken ? { refreshToken } : null
}
async clearRefreshToken({ serverConnectionConfigId }) {
console.log('[AbsDatabase] Clearing refresh token...', serverConnectionConfigId)
localStorage.removeItem(`refresh_token_${serverConnectionConfigId}`)
}
async removeServerConnectionConfig(serverConnectionConfigCallObject) {
var serverConnectionConfigId = serverConnectionConfigCallObject.serverConnectionConfigId
var deviceData = await this.getDeviceData()
@@ -77,6 +82,7 @@ class AbsDatabaseWeb extends WebPlugin {
}
async logout() {
console.log('[AbsDatabase] Logging out...')
var deviceData = await this.getDeviceData()
deviceData.lastServerConnectionConfigId = null
localStorage.setItem('device', JSON.stringify(deviceData))
+20
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) {
return AbsDatabase.setCurrentServerConnectionConfig(serverConnectionConfig).then((data) => {
console.log('Set server connection config', JSON.stringify(data))
+24 -53
View File
@@ -1,7 +1,6 @@
import { CapacitorHttp } from '@capacitor/core'
import { AbsDatabase } from '@/plugins/capacitor'
export default function ({ store }, inject) {
export default function ({ store, $db }, inject) {
const nativeHttp = {
async request(method, _url, data, options = {}) {
// 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
let url = _url
const headers = {}
let headers = {}
if (!url.startsWith('http') && !url.startsWith('capacitor')) {
const bearerToken = store.getters['user/getToken']
if (bearerToken) {
@@ -24,6 +23,10 @@ export default function ({ store }, inject) {
if (data) {
headers['Content-Type'] = 'application/json'
}
if (options.headers) {
headers = { ...headers, ...options.headers }
delete options.headers
}
console.log(`[nativeHttp] Making ${method} request to ${url}`)
return CapacitorHttp.request({
method,
@@ -65,15 +68,15 @@ export default function ({ store }, inject) {
}
// Get refresh token from secure storage
const refreshTokenData = await this.getRefreshToken(serverConnectionConfig.id)
if (!refreshTokenData || !refreshTokenData.refreshToken) {
const refreshToken = await $db.getRefreshToken(serverConnectionConfig.id)
if (!refreshToken) {
console.error('[nativeHttp] No refresh token available')
throw new Error('No refresh token available')
}
// Attempt to refresh the token
const newTokens = await this.refreshAccessToken(refreshTokenData.refreshToken, serverConnectionConfig.address)
if (!newTokens || !newTokens.accessToken) {
const newTokens = await this.refreshAccessToken(refreshToken, serverConnectionConfig.address)
if (!newTokens?.accessToken) {
console.error('[nativeHttp] 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
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({
method,
url,
data,
...options,
headers: newHeaders
headers: {
...headers,
Authorization: `Bearer ${newTokens.accessToken}`
},
...options
})
if (retryResponse.status >= 400) {
@@ -104,26 +107,11 @@ export default function ({ store }, inject) {
console.error('[nativeHttp] Token refresh failed:', error)
// If refresh fails, redirect to login
await this.handleRefreshFailure()
await this.handleRefreshFailure(serverConnectionConfig?.id)
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
* @param {string} refreshToken - The refresh token
@@ -142,8 +130,7 @@ export default function ({ store }, inject) {
url: `${serverAddress}/auth/refresh`,
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${refreshToken}`,
'X-Return-Tokens': 'true'
'x-refresh-token': refreshToken
},
data: {}
})
@@ -162,7 +149,8 @@ export default function ({ store }, inject) {
console.log('[nativeHttp] Successfully refreshed access token')
return {
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) {
console.error('[nativeHttp] Failed to refresh access token:', error)
@@ -190,7 +178,7 @@ export default function ({ store }, inject) {
}
// Save updated config to secure storage
const savedConfig = await AbsDatabase.setCurrentServerConnectionConfig(updatedConfig)
const savedConfig = await $db.setServerConnectionConfig(updatedConfig)
// Update the store
store.commit('user/setAccessToken', tokens.accessToken)
@@ -208,19 +196,15 @@ export default function ({ store }, inject) {
/**
* Handles the case when token refresh fails
* @param {string} [serverConnectionConfigId]
* @returns {Promise} - Promise that resolves when logout is complete
*/
async handleRefreshFailure() {
async handleRefreshFailure(serverConnectionConfigId) {
try {
console.log('[nativeHttp] Handling refresh failure - logging out user')
// Clear the store
store.commit('user/setUser', null)
store.commit('user/setAccessToken', null)
store.commit('user/setServerConnectionConfig', null)
// Logout from database
await AbsDatabase.logout()
// Logout from server and clear store
await store.dispatch('user/logout', { serverConnectionConfigId })
// Redirect to login page
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 = {}) {
return this.request('GET', url, undefined, options)
},