Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Attempt to refresh SSO token before expiry #1941

Closed
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions src/shared/modules/connections/connectionsDuck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,47 @@ export const setAuthEnabled = (authEnabled: any) => {
}
}

function currentTokenExpiresSeconds(state: GlobalState) {
const activeConnection = getActiveConnectionData(state)
const token = activeConnection?.password

if (!token) return

// Get the expiration from the JWT's payload, which is a JSON string encoded
// using base64. You could also use a JWT parsing lib
const [, payloadBase64] = token.split('.')
const payload: { exp: number } = JSON.parse(window.atob(payloadBase64 ?? ''))

if (typeof payload?.exp !== 'number') {
return
}

return payload.exp - Date.now() / 1000
}

// Typically the refresh flow is triggered through a query failing
// with the "token expired error". We schedule a refresh to happen
// before the expiration to avoid the error.
// We still have the old flow since settimeout is not 100% reliable
export function scheduleTokenAuthTokenRefresh(store: any) {
const expiresIn = currentTokenExpiresSeconds(store.getState())
if (expiresIn) {
authLog('Schedule token refresh 60 seconds before expiration')
const refreshIn = expiresIn - 60
setTimeout(() => {
// check if the token has been refreshed or switched in the meantime
const expiresIn = currentTokenExpiresSeconds(store.getState())

if (expiresIn && expiresIn <= 60) {
authLog('Scheduled token refresh triggered')
onLostConnection(store.dispatch)({
code: TokenExpiredDriverError
})
}
}, [refreshIn * 1000])
}
}

export const useDb = (db: any = null) => ({ type: USE_DB, useDb: db })

export const resetUseDb = () => ({ type: USE_DB, useDb: null })
Expand Down Expand Up @@ -563,6 +604,7 @@ export const startupConnectEpic = (action$: any, store: any) => {
store.dispatch(setActiveConnection(discovery.CONNECTION_ID))
if (discovered.attemptSSOLogin) {
authLog('SSO Connection to Neo4j successfully established.')
scheduleTokenAuthTokenRefresh(store)
}
resolve({ type: STARTUP_CONNECTION_SUCCESS })
})
Expand Down Expand Up @@ -703,6 +745,7 @@ export const connectionLostEpic = (action$: any, store: any) =>
authLog(
'Successfully refreshed token, attempting to reconnect'
)
scheduleTokenAuthTokenRefresh(store)
} catch (e) {
authLog(`Failed to refresh token: ${e}`)
authLog(
Expand Down