Skip to content

Commit 10b47ae

Browse files
authored
feat: support custom refresh handling (#715)
1 parent 8306c39 commit 10b47ae

File tree

3 files changed

+117
-55
lines changed

3 files changed

+117
-55
lines changed

src/runtime/plugin.ts

Lines changed: 8 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { getHeader } from 'h3'
22
import authMiddleware from './middleware/auth'
3+
import type { RefreshHandler } from './types'
4+
import defaultRefreshHandler from './utils/refreshHandler'
35
import { getNitroRouteRules } from './utils/kit'
46
import { addRouteMiddleware, defineNuxtPlugin, useRuntimeConfig, useAuth, useAuthState } from '#imports'
57

@@ -35,65 +37,21 @@ export default defineNuxtPlugin(async (nuxtApp) => {
3537
}
3638

3739
// 2. Setup session maintanence, e.g., auto refreshing or refreshing on foux
38-
const { enableRefreshOnWindowFocus, enableRefreshPeriodically } =
39-
runtimeConfig.session
40-
41-
// Listen for when the page is visible, if the user switches tabs
42-
// and makes our tab visible again, re-fetch the session, but only if
43-
// this feature is not disabled.
44-
const visibilityHandler = () => {
45-
if (enableRefreshOnWindowFocus && document.visibilityState === 'visible') {
46-
getSession()
47-
}
48-
}
49-
50-
// Refetch interval
51-
let refetchIntervalTimer: ReturnType<typeof setInterval>
52-
53-
// TODO: find more Generic method to start a Timer for the Refresh Token
54-
// Refetch interval for local/refresh schema
55-
let refreshTokenIntervalTimer: typeof refetchIntervalTimer
40+
const refreshHandler: RefreshHandler =
41+
typeof runtimeConfig.session.refreshHandler === 'undefined'
42+
? defaultRefreshHandler
43+
: runtimeConfig.session.refreshHandler
5644

5745
nuxtApp.hook('app:mounted', () => {
46+
refreshHandler.init(runtimeConfig.session)
5847
if (disableServerSideAuth) {
5948
getSession()
6049
}
61-
62-
document.addEventListener('visibilitychange', visibilityHandler, false)
63-
64-
if (enableRefreshPeriodically !== false) {
65-
const intervalTime =
66-
enableRefreshPeriodically === true ? 1000 : enableRefreshPeriodically
67-
refetchIntervalTimer = setInterval(() => {
68-
if (data.value) {
69-
getSession()
70-
}
71-
}, intervalTime)
72-
}
73-
74-
if (runtimeConfig.provider.type === 'refresh') {
75-
const intervalTime = runtimeConfig.provider.token.maxAgeInSeconds! * 1000
76-
const { refresh, refreshToken } = useAuth()
77-
refreshTokenIntervalTimer = setInterval(() => {
78-
if (refreshToken.value) {
79-
refresh()
80-
}
81-
}, intervalTime)
82-
}
8350
})
8451

8552
const _unmount = nuxtApp.vueApp.unmount
8653
nuxtApp.vueApp.unmount = function () {
87-
// Clear visibility handler
88-
document.removeEventListener('visibilitychange', visibilityHandler, false)
89-
90-
// Clear refetch interval
91-
clearInterval(refetchIntervalTimer)
92-
93-
// Clear refetch interval
94-
if (refreshTokenIntervalTimer) {
95-
clearInterval(refreshTokenIntervalTimer)
96-
}
54+
refreshHandler.destroy()
9755

9856
// Clear session
9957
lastRefreshedAt.value = undefined

src/runtime/types.ts

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -335,11 +335,8 @@ export type AuthProviders =
335335
| ProviderLocal
336336
| ProviderLocalRefresh;
337337

338-
/**
339-
* Configuration for the application-side session.
340-
*/
341-
type SessionConfig = {
342-
/**
338+
export type RefreshHandlerConfig = {
339+
/**
343340
* Whether to refresh the session every `X` milliseconds. Set this to `false` to turn it off. The session will only be refreshed if a session already exists.
344341
*
345342
* Setting this to `true` will refresh the session every second.
@@ -360,6 +357,34 @@ type SessionConfig = {
360357
enableRefreshOnWindowFocus: boolean;
361358
};
362359

360+
export type RefreshHandler = {
361+
/**
362+
* Initializes the refresh handler with the given configuration.
363+
* init will be called inside app:mouted lifecycle hook.
364+
*
365+
* @param config The configuration to use for the refresh handler.
366+
*/
367+
init: (config: RefreshHandlerConfig) => void;
368+
369+
/**
370+
* Handles cleanup of the refresh handler. This method will be called when the app is destroyed.
371+
*/
372+
destroy: () => void;
373+
};
374+
375+
/**
376+
* Configuration for the application-side session.
377+
*/
378+
type SessionConfig = RefreshHandlerConfig & {
379+
/**
380+
* A custom refresh handler to use. This can be used to implement custom session refresh logic. If not set, the default refresh handler will be used.
381+
*
382+
* @example MyCustomRefreshHandler
383+
* @default undefined
384+
*/
385+
refreshHandler?: RefreshHandler;
386+
};
387+
363388
/**
364389
* Configuration for the whole module.
365390
*/
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import type { RefreshHandlerConfig, RefreshHandler } from '../types'
2+
import { useRuntimeConfig, useAuth, useAuthState } from '#imports'
3+
4+
interface DefaultRefreshHandler extends RefreshHandler {
5+
config?: RefreshHandlerConfig
6+
refetchIntervalTimer?: ReturnType<typeof setInterval>
7+
refreshTokenIntervalTimer?: ReturnType<typeof setInterval>
8+
visibilityHandler(): void
9+
}
10+
11+
const defaultRefreshHandler: DefaultRefreshHandler = {
12+
// Session configuration keep this for reference
13+
config: undefined,
14+
15+
// Refetch interval
16+
refetchIntervalTimer: undefined,
17+
18+
// TODO: find more Generic method to start a Timer for the Refresh Token
19+
// Refetch interval for local/refresh schema
20+
refreshTokenIntervalTimer: undefined,
21+
22+
visibilityHandler () {
23+
// Listen for when the page is visible, if the user switches tabs
24+
// and makes our tab visible again, re-fetch the session, but only if
25+
// this feature is not disabled.
26+
if (this.config?.enableRefreshOnWindowFocus && document.visibilityState === 'visible') {
27+
useAuth().getSession()
28+
}
29+
},
30+
31+
init (config: RefreshHandlerConfig): void {
32+
this.config = config
33+
34+
const runtimeConfig = useRuntimeConfig().public.auth
35+
36+
const { data } = useAuthState()
37+
const { getSession } = useAuth()
38+
39+
document.addEventListener('visibilitychange', this.visibilityHandler, false)
40+
41+
const { enableRefreshPeriodically } = config
42+
43+
if (enableRefreshPeriodically !== false) {
44+
const intervalTime =
45+
enableRefreshPeriodically === true ? 1000 : enableRefreshPeriodically
46+
this.refetchIntervalTimer = setInterval(() => {
47+
if (data.value) {
48+
getSession()
49+
}
50+
}, intervalTime)
51+
}
52+
53+
if (runtimeConfig.provider.type === 'refresh') {
54+
const intervalTime = runtimeConfig.provider.token.maxAgeInSeconds! * 1000
55+
const { refresh, refreshToken } = useAuth()
56+
57+
this.refreshTokenIntervalTimer = setInterval(() => {
58+
if (refreshToken.value) {
59+
refresh()
60+
}
61+
}, intervalTime)
62+
}
63+
},
64+
65+
destroy (): void {
66+
// Clear visibility handler
67+
document.removeEventListener('visibilitychange', this.visibilityHandler, false)
68+
69+
// Clear refetch interval
70+
clearInterval(this.refetchIntervalTimer)
71+
72+
// Clear refetch interval
73+
if (this.refreshTokenIntervalTimer) {
74+
clearInterval(this.refreshTokenIntervalTimer)
75+
}
76+
}
77+
}
78+
79+
export default defaultRefreshHandler

0 commit comments

Comments
 (0)