Fix oidc button not showing on re-login, fix oidc re-login showing config already exists #1638 #1634

This commit is contained in:
advplyr 2025-08-16 17:55:00 -05:00
parent 7aebcd92c3
commit 4be1598eca
8 changed files with 46 additions and 24 deletions

View file

@ -407,6 +407,9 @@ class ApiHandler(var ctx:Context) {
if (checkAbsDatabaseNotifyListenersInitted()) { if (checkAbsDatabaseNotifyListenersInitted()) {
val tokenJsObject = JSObject() val tokenJsObject = JSObject()
tokenJsObject.put("error", "Token refresh failed") tokenJsObject.put("error", "Token refresh failed")
if (serverConnectionConfigId.isNotEmpty()) {
tokenJsObject.put("serverConnectionConfigId", serverConnectionConfigId)
}
absDatabaseNotifyListeners("onTokenRefreshFailure", tokenJsObject) absDatabaseNotifyListeners("onTokenRefreshFailure", tokenJsObject)
} else { } else {
// Can happen if Webview is never run // Can happen if Webview is never run

View file

@ -30,7 +30,7 @@
<!-- form to add a new server connection config --> <!-- form to add a new server connection config -->
<div v-else class="w-full"> <div v-else class="w-full">
<!-- server address input --> <!-- server address input -->
<form v-if="!showAuth" @submit.prevent="submit" novalidate class="w-full"> <form v-if="!showAuth" @submit.prevent="submit(false)" novalidate class="w-full">
<div v-if="serverConnectionConfigs.length" class="flex items-center mb-4" @click="showServerList"> <div v-if="serverConnectionConfigs.length" class="flex items-center mb-4" @click="showServerList">
<span class="material-symbols text-fg-muted">arrow_back</span> <span class="material-symbols text-fg-muted">arrow_back</span>
</div> </div>
@ -412,7 +412,7 @@ export default {
throw new Error('Authentication failed with the provided token.') throw new Error('Authentication failed with the provided token.')
} }
const duplicateConfig = this.serverConnectionConfigs.find((scc) => scc.address === this.serverConfig.address && scc.username === payload.user.username) const duplicateConfig = this.serverConnectionConfigs.find((scc) => scc.address === this.serverConfig.address && scc.username === payload.user.username && scc.id !== this.serverConfig.id)
if (duplicateConfig) { if (duplicateConfig) {
throw new Error('Config already exists for this address and username.') throw new Error('Config already exists for this address and username.')
} }
@ -471,7 +471,11 @@ export default {
// Will NOT include access token and refresh token // Will NOT include access token and refresh token
this.setUserAndConnection(payload) this.setUserAndConnection(payload)
} else { } else {
this.showAuth = true let error = this.error
if (await this.submit(true)) {
this.showForm = true
this.error = error
}
} }
}, },
async removeServerConfigClick() { async removeServerConfigClick() {
@ -500,12 +504,14 @@ export default {
this.showForm = !this.serverConnectionConfigs.length this.showForm = !this.serverConnectionConfigs.length
} }
}, },
editServerConfig(serverConfig) { async editServerConfig(serverConfig) {
this.serverConfig = { this.serverConfig = {
...serverConfig ...serverConfig
} }
this.showForm = true
this.showAuth = true if (await this.submit(true)) {
this.showForm = true
}
}, },
async newServerConfigClick() { async newServerConfigClick() {
await this.$hapticsImpact() await this.$hapticsImpact()
@ -651,9 +657,8 @@ export default {
return false return false
}) })
}, },
async submit() { async submit(preventAutoLogin = false) {
if (!this.networkConnected) return if (!this.networkConnected || !this.serverConfig.address) return false
if (!this.serverConfig.address) return
const initialAddress = this.serverConfig.address const initialAddress = this.serverConfig.address
// Did the user specify a protocol? // Did the user specify a protocol?
@ -666,6 +671,7 @@ export default {
this.authMethods = [] this.authMethods = []
try { try {
console.log('[ServerConnectForm] submit tryServerUrl: ' + this.serverConfig.address)
// Try the server URL. If it fails and the protocol was not provided, try with http instead of https // Try the server URL. If it fails and the protocol was not provided, try with http instead of https
const statusData = await this.tryServerUrl(this.serverConfig.address, !protocolProvided) const statusData = await this.tryServerUrl(this.serverConfig.address, !protocolProvided)
if (this.validateLoginFormResponse(statusData, this.serverConfig.address, protocolProvided)) { if (this.validateLoginFormResponse(statusData, this.serverConfig.address, protocolProvided)) {
@ -674,11 +680,12 @@ export default {
this.oauth.buttonText = statusData.data.authFormData?.authOpenIDButtonText || 'Login with OpenID' this.oauth.buttonText = statusData.data.authFormData?.authOpenIDButtonText || 'Login with OpenID'
this.serverConfig.version = statusData.data.serverVersion this.serverConfig.version = statusData.data.serverVersion
if (statusData.data.authFormData?.authOpenIDAutoLaunch) { if (statusData.data.authFormData?.authOpenIDAutoLaunch && !preventAutoLogin) {
this.clickLoginWithOpenId() this.clickLoginWithOpenId()
} }
return true return true
} else { } else {
console.log('[ServerConnectForm] submit validateLoginFormResponse failed: ' + this.serverConfig.address)
return false return false
} }
} catch (error) { } catch (error) {
@ -852,7 +859,7 @@ export default {
} else { } else {
// Detect if the connection config is using the old token. If so, force re-login // Detect if the connection config is using the old token. If so, force re-login
if (this.serverConfig.token === user.token || user.isOldToken) { if (this.serverConfig.token === user.token || user.isOldToken) {
this.setForceReloginForNewAuth() this.setForceRelogin('oldAuthToken')
return return
} }
@ -926,20 +933,29 @@ export default {
this.processing = false this.processing = false
return authRes return authRes
}, },
async setForceReloginForNewAuth() { async setForceRelogin(error) {
console.log('[ServerConnectForm] setForceRelogin', error, this.serverConfig.address)
// This calls /status on the server and sets the auth methods // This calls /status on the server and sets the auth methods
const result = await this.submit() const result = await this.submit(true)
if (result) { if (result) {
this.error = this.$strings.MessageOldServerAuthReLoginRequired this.showForm = true
if (error === 'oldAuthToken') {
this.error = this.$strings.MessageOldServerAuthReLoginRequired
} else if (error === 'refreshTokenFailed') {
this.error = this.$strings.MessageFailedToRefreshToken
}
} }
}, },
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.serverConnectionConfigId) {
if (this.$route.query.error === 'oldAuthToken' && this.$route.query.serverConnectionConfigId) { // Handle force re-login for servers using new JWT auth but still using an old token OR refresh token failed
this.serverConfig = this.serverConnectionConfigs.find((scc) => scc.id === this.$route.query.serverConnectionConfigId) this.serverConfig = this.serverConnectionConfigs.find((scc) => scc.id === this.$route.query.serverConnectionConfigId)
if (this.serverConfig) { if (this.serverConfig) {
this.setForceReloginForNewAuth() this.setForceRelogin(this.$route.query.error)
return return
} else {
console.error('[ServerConnectForm] init with serverConnectionConfigId but no serverConfig found', this.$route.query.serverConnectionConfigId)
} }
} }

View file

@ -173,16 +173,18 @@ export default {
try { try {
console.log('[PdfReader] Handling refresh failure - logging out user') console.log('[PdfReader] Handling refresh failure - logging out user')
const serverConnectionConfigId = this.$store.getters['user/getServerConnectionConfigId']
// Clear store // Clear store
await this.$store.dispatch('user/logout') await this.$store.dispatch('user/logout')
if (this.$store.getters['user/getServerConnectionConfigId']) { if (serverConnectionConfigId) {
// Clear refresh token for server connection config // Clear refresh token for server connection config
await this.$db.clearRefreshToken(this.$store.getters['user/getServerConnectionConfigId']) await this.$db.clearRefreshToken(serverConnectionConfigId)
} }
if (window.location.pathname !== '/connect') { if (window.location.pathname !== '/connect') {
window.location.href = '/connect' window.location.href = '/connect?error=refreshTokenFailed&serverConnectionConfigId=' + serverConnectionConfigId
} }
} catch (error) { } catch (error) {
console.error('[PdfReader] Failed to handle refresh failure:', error) console.error('[PdfReader] Failed to handle refresh failure:', error)

View file

@ -37,9 +37,9 @@ export default {
computed: {}, computed: {},
methods: { methods: {
async init() { async init() {
await this.$store.dispatch('setupNetworkListener')
this.deviceData = await this.$db.getDeviceData() this.deviceData = await this.$db.getDeviceData()
this.$store.commit('setDeviceData', this.deviceData) this.$store.commit('setDeviceData', this.deviceData)
await this.$store.dispatch('setupNetworkListener')
} }
}, },
mounted() { mounted() {

View file

@ -32,7 +32,7 @@ export default function ({ $axios, store, $db }) {
} }
if (window.location.pathname !== '/connect') { if (window.location.pathname !== '/connect') {
window.location.href = '/connect' window.location.href = '/connect?error=refreshTokenFailed&serverConnectionConfigId=' + serverConnectionConfigId
} }
} catch (error) { } catch (error) {
console.error('[axios] Failed to handle refresh failure:', error) console.error('[axios] Failed to handle refresh failure:', error)

View file

@ -138,7 +138,7 @@ export default ({ app, store }, inject) => {
// Clear store and redirect to login page // Clear store and redirect to login page
await store.dispatch('user/logout') await store.dispatch('user/logout')
if (window.location.pathname !== '/connect') { if (window.location.pathname !== '/connect') {
window.location.href = '/connect' window.location.href = '/connect?error=refreshTokenFailed&serverConnectionConfigId=' + data.serverConnectionConfigId || ''
} }
}) })
} }

View file

@ -221,7 +221,7 @@ export default function ({ store, $db, $socket }, inject) {
// Redirect to login page // Redirect to login page
if (window.location.pathname !== '/connect') { if (window.location.pathname !== '/connect') {
window.location.href = '/connect' window.location.href = '/connect?error=refreshTokenFailed&serverConnectionConfigId=' + serverConnectionConfigId
} }
} catch (error) { } catch (error) {
console.error('[nativeHttp] Failed to handle refresh failure:', error) console.error('[nativeHttp] Failed to handle refresh failure:', error)

View file

@ -298,6 +298,7 @@
"MessageDownloading": "Downloading...", "MessageDownloading": "Downloading...",
"MessageDownloadingEpisode": "Downloading episode", "MessageDownloadingEpisode": "Downloading episode",
"MessageEpisodesQueuedForDownload": "{0} Episode(s) queued for download", "MessageEpisodesQueuedForDownload": "{0} Episode(s) queued for download",
"MessageFailedToRefreshToken": "Failed to refresh token, re-login required",
"MessageFeedURLWillBe": "Feed URL will be {0}", "MessageFeedURLWillBe": "Feed URL will be {0}",
"MessageFetching": "Fetching...", "MessageFetching": "Fetching...",
"MessageFollowTheProjectOnGithub": "Follow the project on Github", "MessageFollowTheProjectOnGithub": "Follow the project on Github",