OIDC checking / retrying 2

This commit is contained in:
simonredfern 2025-12-14 11:15:35 +01:00
parent cd9ba264ec
commit 0b4825fc7c
3 changed files with 72 additions and 29 deletions

View File

@ -162,6 +162,10 @@ let instance: any
console.log(' Client ID:', process.env.VITE_OBP_OAUTH2_CLIENT_ID || 'NOT SET')
console.log(' Redirect URI:', process.env.VITE_OBP_OAUTH2_REDIRECT_URL || 'NOT SET')
console.log('OAuth2/OIDC ready for authentication')
// Start continuous monitoring even when initially connected
oauth2Service.startHealthCheck(1000, 240000) // Monitor every 4 minutes
console.log('OAuth2Service: Starting continuous monitoring (every 4 minutes)')
} else {
console.error('OAuth2Service: Initialization failed after all retries')
@ -176,7 +180,7 @@ let instance: any
console.warn(' 3. Network connectivity to OIDC provider')
// Start periodic health check to reconnect when OIDC becomes available
oauth2Service.startHealthCheck(1000) // Start with 1 second, then exponential backoff
oauth2Service.startHealthCheck(1000, 240000) // Start with 1 second, monitor every 4 minutes when connected
}
}
console.log(`-----------------------------------------------------------------`)

View File

@ -211,15 +211,17 @@ export class OAuth2Service {
}
/**
* Start periodic health check to reconnect if OIDC server becomes available
* Uses exponential backoff: 1min, 2min, 4min, capped at 4min
* Start periodic health check to monitor OIDC availability
* Uses exponential backoff when reconnecting: 1s, 2s, 4s, up to 4min
* Switches to regular monitoring (every 4 minutes) once connected
*
* @param {number} initialIntervalMs - Initial interval in milliseconds (default: 1000 = 1 second)
* @param {number} monitoringIntervalMs - Interval for continuous monitoring when connected (default: 240000 = 4 minutes)
*
* @example
* oauth2Service.startHealthCheck(1000) // Start checking at 1 second, then exponential backoff
* oauth2Service.startHealthCheck(1000, 240000) // Start checking at 1 second, monitor every 4 minutes when connected
*/
startHealthCheck(initialIntervalMs: number = 1000): void {
startHealthCheck(initialIntervalMs: number = 1000, monitoringIntervalMs: number = 240000): void {
if (this.healthCheckInterval) {
console.log('OAuth2Service: Health check already running')
return
@ -235,32 +237,70 @@ export class OAuth2Service {
console.log('OAuth2Service: Starting health check with exponential backoff')
const scheduleNextCheck = () => {
if (!this.initialized && this.wellKnownUrl) {
// Calculate delay with exponential backoff, capped at 4 minutes
const delay = Math.min(initialIntervalMs * Math.pow(2, this.healthCheckAttempts), 240000)
const delayDisplay =
delay < 60000
? `${(delay / 1000).toFixed(0)} second(s)`
: `${(delay / 60000).toFixed(1)} minute(s)`
if (!this.wellKnownUrl) {
return
}
let delay: number
if (this.initialized) {
// When connected, monitor every 4 minutes
delay = monitoringIntervalMs
} else {
// When disconnected, use exponential backoff
delay = Math.min(initialIntervalMs * Math.pow(2, this.healthCheckAttempts), 240000)
}
const delayDisplay =
delay < 60000
? `${(delay / 1000).toFixed(0)} second(s)`
: `${(delay / 60000).toFixed(1)} minute(s)`
if (this.initialized) {
console.log(`OAuth2Service: Monitoring scheduled in ${delayDisplay}`)
} else {
console.log(
`OAuth2Service: Health check scheduled in ${delayDisplay} (attempt ${this.healthCheckAttempts + 1})`
)
}
this.healthCheckInterval = setTimeout(async () => {
this.healthCheckInterval = setTimeout(async () => {
if (this.initialized) {
// When connected, verify OIDC is still available
console.log('OAuth2Service: Verifying OIDC availability...')
try {
const response = await fetch(this.wellKnownUrl)
if (!response.ok) {
throw new Error(`OIDC server returned ${response.status}`)
}
console.log('OAuth2Service: OIDC is available')
// Continue monitoring
scheduleNextCheck()
} catch (error) {
console.error('OAuth2Service: OIDC server is no longer available!')
this.initialized = false
this.oidcConfig = null
this.healthCheckAttempts = 0
console.log('OAuth2Service: Attempting to reconnect...')
// Schedule reconnection with exponential backoff
scheduleNextCheck()
}
} else {
// When disconnected, attempt to reconnect
console.log('OAuth2Service: Health check - attempting to reconnect to OIDC server...')
try {
await this.initializeFromWellKnown(this.wellKnownUrl)
console.log('OAuth2Service: Successfully reconnected to OIDC server!')
// Stop health check once reconnected
this.stopHealthCheck()
this.healthCheckAttempts = 0
// Switch to continuous monitoring
scheduleNextCheck()
} catch (error) {
this.healthCheckAttempts++
// Schedule next check with longer interval
// Schedule next reconnection attempt
scheduleNextCheck()
}
}, delay)
}
}
}, delay)
}
// Start the first check

View File

@ -87,14 +87,15 @@ async function checkOAuth2Availability() {
try {
const response = await fetch('/api/status/oauth2')
const data = await response.json()
const wasAvailable = oauth2Available.value
oauth2Available.value = data.available
oauth2StatusMessage.value = data.message || ''
if (data.available && oauth2CheckInterval) {
// Stop polling once OAuth2 is available
clearInterval(oauth2CheckInterval)
oauth2CheckInterval = null
console.log('OAuth2 is now available, stopped polling')
// Log state changes
if (!wasAvailable && data.available) {
console.log('OAuth2 is now available')
} else if (wasAvailable && !data.available) {
console.warn('OAuth2 is no longer available!')
}
} catch (error) {
oauth2Available.value = false
@ -153,18 +154,16 @@ onMounted(async () => {
// Initial OAuth2 availability check
await checkOAuth2Availability()
// If OAuth2 is not available, poll every 30 seconds
if (!oauth2Available.value) {
console.log('OAuth2 not available, starting periodic check...')
oauth2CheckInterval = window.setInterval(checkOAuth2Availability, 30000)
}
// Start continuous polling every 4 minutes to detect OIDC outages
console.log('OAuth2: Starting continuous monitoring (every 4 minutes)...')
oauth2CheckInterval = window.setInterval(checkOAuth2Availability, 240000) // 4 minutes
const currentUser = await getCurrentUser()
const currentResponseKeys = Object.keys(currentUser)
if (currentResponseKeys.includes('username')) {
loginUsername.value = currentUser.username
isShowLoginButton.value = false
isShowLogOffButton.value = !isShowLoginButton.value
loginUsername.value = currentUser.username
} else {
isShowLoginButton.value = true
isShowLogOffButton.value = !isShowLoginButton.value