@sbc-connect/nuxt-auth 0.1.7 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.env.example CHANGED
@@ -22,4 +22,10 @@ NUXT_PUBLIC_LD_CLIENT_ID=""
22
22
  NUXT_PUBLIC_IDP_URL="https://dev.loginproxy.gov.bc.ca/auth"
23
23
  NUXT_PUBLIC_IDP_REALM="bcregistry"
24
24
  NUXT_PUBLIC_IDP_CLIENTID="connect-web"
25
- NUXT_PUBLIC_SITEMINDER_LOGOUT_URL="https://logontest7.gov.bc.ca/clp-cgi/logoff.cgi"
25
+ NUXT_PUBLIC_SITEMINDER_LOGOUT_URL="https://logontest7.gov.bc.ca/clp-cgi/logoff.cgi"
26
+
27
+ # Session Timeout
28
+ NUXT_PUBLIC_TOKEN_REFRESH_INTERVAL=30000
29
+ NUXT_PUBLIC_TOKEN_MIN_VALIDITY=120000
30
+ NUXT_PUBLIC_SESSION_INACTIVITY_TIMEOUT=1800000
31
+ NUXT_PUBLIC_SESSION_MODAL_TIMEOUT=120000
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @sbc-connect/nuxt-auth
2
2
 
3
+ ## 0.1.8
4
+
5
+ ### Patch Changes
6
+
7
+ - [#34](https://github.com/bcgov/connect-nuxt/pull/34) [`b2a0458`](https://github.com/bcgov/connect-nuxt/commit/b2a04587d5408d213d463ef6161b701ca597ef86) Thanks [@deetz99](https://github.com/deetz99)! - - handle user session expiry in auth plugin
8
+
9
+ - onBeforeSessionExpiry route meta
10
+ - onAccountChange route meta
11
+ - resetPiniaStores util and reset on logout
12
+ - add account id to layouts/ConnectAuth breadcrumb slot
13
+
14
+ issue: bcgov/entity#29335
15
+
16
+ - [#32](https://github.com/bcgov/connect-nuxt/pull/32) [`66d83d1`](https://github.com/bcgov/connect-nuxt/commit/66d83d14b2ec7950057dd39a4d876a8c4096923f) Thanks [@kialj876](https://github.com/kialj876)! - Pay layer cleanup bcgov/entity#29337
17
+
18
+ - Updated dependencies [[`66d83d1`](https://github.com/bcgov/connect-nuxt/commit/66d83d14b2ec7950057dd39a4d876a8c4096923f)]:
19
+ - @sbc-connect/nuxt-base@0.1.8
20
+
3
21
  ## 0.1.7
4
22
 
5
23
  ### Patch Changes
@@ -26,7 +26,7 @@ defineProps({
26
26
  </span>
27
27
  <span
28
28
  class="line-clamp-1 overflow-hidden text-ellipsis text-xs opacity-75"
29
- :class="{ 'text-line': theme === 'header', 'text-neutral': theme === 'dropdown' }"
29
+ :class="{ 'text-line-muted': theme === 'header', 'text-neutral': theme === 'dropdown' }"
30
30
  >
31
31
  {{ accountName }}
32
32
  </span>
@@ -0,0 +1,93 @@
1
+ <script setup lang="ts">
2
+ const isSmallScreen = useMediaQuery('(max-width: 640px)')
3
+ const rtc = useRuntimeConfig().public
4
+ const modalTimeout = rtc.sessionModalTimeout ? Number(rtc.sessionModalTimeout) : 120000
5
+ const { t } = useI18n()
6
+ const route = useRoute()
7
+
8
+ const emit = defineEmits<{ close: [] }>()
9
+
10
+ const timeRemaining = ref(toValue((modalTimeout) / 1000))
11
+
12
+ const intervalId = setInterval(async () => {
13
+ const value = timeRemaining.value - 1
14
+ timeRemaining.value = value < 0 ? 0 : value
15
+
16
+ if (value === 0) {
17
+ if (route.meta.onBeforeSessionExpired) {
18
+ await route.meta.onBeforeSessionExpired()
19
+ }
20
+ sessionStorage.setItem(ConnectAuthStorageKey.CONNECT_SESSION_EXPIRED, 'true')
21
+ await useConnectAuth().logout()
22
+ }
23
+ }, 1000)
24
+
25
+ const ariaCountdownText = computed(() => {
26
+ if (timeRemaining.value === 30) { // trigger aria alert when 30 seconds remain
27
+ return t('connect.sessionExpiry.content', { count: timeRemaining.value })
28
+ } else if (timeRemaining.value === 2) { // trigger aria alert when session expires
29
+ return t('connect.sessionExpiry.sessionExpired')
30
+ } else {
31
+ return ''
32
+ }
33
+ })
34
+
35
+ function closeModal() {
36
+ clearInterval(intervalId)
37
+ emit('close')
38
+ }
39
+
40
+ onMounted(() => {
41
+ // allow any keypress to close the modal
42
+ window.addEventListener('keydown', closeModal)
43
+ })
44
+
45
+ onUnmounted(() => {
46
+ // cleanup
47
+ window.removeEventListener('keydown', closeModal)
48
+ })
49
+ </script>
50
+
51
+ <template>
52
+ <UModal
53
+ id="session-expired-dialog"
54
+ overlay
55
+ :title="$t('connect.sessionExpiry.title')"
56
+ :description="$t('connect.sessionExpiry.content', { count: timeRemaining })"
57
+ @after:leave="closeModal"
58
+ >
59
+ <template #content>
60
+ <div class="p-10 flex flex-col gap-6">
61
+ <div role="alert">
62
+ <h2
63
+ id="session-expired-dialog-title"
64
+ class="text-xl font-bold text-neutral-highlighted"
65
+ >
66
+ {{ $t('connect.sessionExpiry.title') }}
67
+ </h2>
68
+ </div>
69
+ <div>
70
+ <ConnectI18nHelper
71
+ id="session-expired-dialog-description"
72
+ translation-path="connect.sessionExpiry.content"
73
+ :count="timeRemaining"
74
+ />
75
+
76
+ <div role="alert">
77
+ <span class="sr-only">{{ ariaCountdownText }}</span>
78
+ </div>
79
+ </div>
80
+ <div class="flex flex-wrap items-center justify-center gap-4">
81
+ <UButton
82
+ :block="isSmallScreen"
83
+ :label="$t('connect.sessionExpiry.continueBtn.main')"
84
+ :aria-label="$t('connect.sessionExpiry.continueBtn.aria')"
85
+ size="xl"
86
+ class="font-bold"
87
+ @click="closeModal"
88
+ />
89
+ </div>
90
+ </div>
91
+ </template>
92
+ </UModal>
93
+ </template>
@@ -9,7 +9,7 @@ export const useConnectAuth = () => {
9
9
  * @returns A promise that resolves when login is complete.
10
10
  */
11
11
  function login(idpHint: ConnectIdpHint, redirect?: string): Promise<void> {
12
- const loginRedirectUrl = sessionStorage.getItem(ConnectAuthStorageKeys.LOGIN_REDIRECT_URL)
12
+ const loginRedirectUrl = sessionStorage.getItem(ConnectAuthStorageKey.LOGIN_REDIRECT_URL)
13
13
  const redirectUri = redirect ?? loginRedirectUrl ?? window.location.href
14
14
 
15
15
  return $connectAuth.login(
@@ -27,14 +27,14 @@ export const useConnectAuth = () => {
27
27
  */
28
28
  function logout(redirect?: string): Promise<void> {
29
29
  const siteminderUrl = rtc.siteminderLogoutUrl
30
- const logoutRedirectUrl = sessionStorage.getItem(ConnectAuthStorageKeys.LOGOUT_REDIRECT_URL)
30
+ const logoutRedirectUrl = sessionStorage.getItem(ConnectAuthStorageKey.LOGOUT_REDIRECT_URL)
31
31
  let redirectUri = redirect ?? logoutRedirectUrl ?? window.location.href
32
32
 
33
33
  if (siteminderUrl) {
34
34
  redirectUri = `${siteminderUrl}?returl=${redirectUri.replace(/(https?:\/\/)|(\/)+/g, '$1$2')}&retnow=1`
35
35
  }
36
36
 
37
- // resetPiniaStores()
37
+ resetPiniaStores()
38
38
  return $connectAuth.logout({
39
39
  redirectUri
40
40
  })
@@ -82,19 +82,19 @@ export const useConnectAuth = () => {
82
82
  }
83
83
 
84
84
  function setLoginRedirectUrl(url: string) {
85
- sessionStorage.setItem(ConnectAuthStorageKeys.LOGIN_REDIRECT_URL, url)
85
+ sessionStorage.setItem(ConnectAuthStorageKey.LOGIN_REDIRECT_URL, url)
86
86
  }
87
87
 
88
88
  function setLogoutRedirectUrl(url: string) {
89
- sessionStorage.setItem(ConnectAuthStorageKeys.LOGOUT_REDIRECT_URL, url)
89
+ sessionStorage.setItem(ConnectAuthStorageKey.LOGOUT_REDIRECT_URL, url)
90
90
  }
91
91
 
92
92
  function clearLoginRedirectUrl() {
93
- sessionStorage.removeItem(ConnectAuthStorageKeys.LOGIN_REDIRECT_URL)
93
+ sessionStorage.removeItem(ConnectAuthStorageKey.LOGIN_REDIRECT_URL)
94
94
  }
95
95
 
96
96
  function clearLogoutRedirectUrl() {
97
- sessionStorage.removeItem(ConnectAuthStorageKeys.LOGOUT_REDIRECT_URL)
97
+ sessionStorage.removeItem(ConnectAuthStorageKey.LOGOUT_REDIRECT_URL)
98
98
  }
99
99
 
100
100
  return {
@@ -87,9 +87,7 @@ export function useConnectHeaderOptions() {
87
87
  onSelect: () => {
88
88
  if (!isActive && account.id) {
89
89
  if (route.meta.onAccountChange) {
90
- // TODO: add route meta option
91
- const allowAccountChange = true
92
- // const allowAccountChange = route.meta.onAccountChange(accountStore.currentAccount, account)
90
+ const allowAccountChange = route.meta.onAccountChange(accountStore.currentAccount, account)
93
91
  if (allowAccountChange) {
94
92
  accountStore.switchCurrentAccount(account.id)
95
93
  }
@@ -1,4 +1,4 @@
1
- export enum ConnectAuthStorageKeys {
1
+ export enum ConnectAuthStorageKey {
2
2
  LOGIN_REDIRECT_URL = 'sbc-connect-login-redirect-url',
3
3
  LOGOUT_REDIRECT_URL = 'sbc-connect-logout-redirect-url',
4
4
  CONNECT_SESSION_EXPIRED = 'connect-session-expired'
@@ -1,8 +1,19 @@
1
+ <script setup lang="ts">
2
+ const accountStore = useConnectAccountStore()
3
+ </script>
4
+
1
5
  <template>
2
6
  <ConnectLayout>
3
7
  <template #header>
4
8
  <ConnectHeaderAuth />
5
9
  </template>
10
+ <template #breadcrumb>
11
+ <ConnectBreadcrumb
12
+ :account-id="accountStore.currentAccount.id
13
+ ? String(accountStore.currentAccount.id)
14
+ : undefined"
15
+ />
16
+ </template>
6
17
  <slot />
7
18
  </ConnectLayout>
8
19
  </template>
@@ -1,4 +1,5 @@
1
1
  import Keycloak from 'keycloak-js'
2
+ import { ConnectModalSessionExpired } from '#components'
2
3
 
3
4
  export default defineNuxtPlugin(async () => {
4
5
  const rtc = useRuntimeConfig().public
@@ -10,6 +11,10 @@ export default defineNuxtPlugin(async () => {
10
11
  clientId: rtc.idpClientid
11
12
  })
12
13
 
14
+ const tokenRefreshInterval = rtc.tokenRefreshInterval ? Number(rtc.tokenRefreshInterval) : 30000
15
+ const tokenMinValidity = rtc.tokenMinValidity ? Number(rtc.tokenMinValidity) / 1000 : 120
16
+ const sessionInactivityTimeout = rtc.sessionInactivityTimeout ? Number(rtc.sessionInactivityTimeout) : 1800000
17
+
13
18
  try {
14
19
  // default behaviour when keycloak session expires
15
20
  // try to update token - log out if token update fails
@@ -18,7 +23,7 @@ export default defineNuxtPlugin(async () => {
18
23
  keycloak.onTokenExpired = async () => {
19
24
  try {
20
25
  console.info('[Auth] Token expired, refreshing token...')
21
- await keycloak.updateToken(minValidity)
26
+ await keycloak.updateToken(tokenMinValidity)
22
27
  console.info('[Auth] Token refreshed.')
23
28
  } catch (error) {
24
29
  console.error('[Auth] Failed to refresh token on expiration; logging out.', error)
@@ -36,34 +41,24 @@ export default defineNuxtPlugin(async () => {
36
41
  console.error('[Auth] Failed to initialize Keycloak adapter: ', error)
37
42
  }
38
43
 
39
- // TODO: add to env
40
- const refreshIntervalTimeout = 30000 // rtc.tokenRefreshInterval as number
41
- const minValidity = 120 // toValue((rtc.tokenMinValidity as number) / 1000) // convert to seconds
42
- const idleTimeout = 1800000 // rtc.sessionIdleTimeout as number
43
-
44
- // const route = useRoute()
45
- const { idle } = useIdle(idleTimeout)
44
+ const { idle } = useIdle(sessionInactivityTimeout)
46
45
 
47
46
  // executed when user is authenticated and idle = true
48
- // TODO: manage session expiry
49
47
  async function sessionExpired() {
50
- // if (route.meta.sessionExpiredFn) { // if route meta provided, override default behaviour
51
- // await route.meta.sessionExpiredFn()
52
- // } else { // open expiry modal
53
- // await useConnectModals().openSessionExpiringModal()
54
- // }
55
- console.info('TODO - MANAGE SESSION EXPIRY')
48
+ const overlay = useOverlay()
49
+ const modal = overlay.create(ConnectModalSessionExpired)
50
+ modal.open()
56
51
  }
57
52
 
58
- // refresh token if expiring within <minValidity> - checks every <refreshIntervalTimeout>
53
+ // refresh token if expiring within <tokenMinValidity> - checks every <tokenRefreshInterval>
59
54
  function scheduleRefreshToken() {
60
55
  console.info('[Auth] Verifying token validity.')
61
56
 
62
57
  setTimeout(async () => {
63
- if (keycloak.isTokenExpired(minValidity)) {
58
+ if (keycloak.isTokenExpired(tokenMinValidity)) {
64
59
  console.info('[Auth] Token set to expire soon. Refreshing token...')
65
60
  try {
66
- await keycloak.updateToken(minValidity)
61
+ await keycloak.updateToken(tokenMinValidity)
67
62
  console.info('[Auth] Token refreshed.')
68
63
  } catch (error) {
69
64
  console.error('[Auth] Failed to refresh token; logging out.', error)
@@ -72,7 +67,7 @@ export default defineNuxtPlugin(async () => {
72
67
  }
73
68
 
74
69
  scheduleRefreshToken()
75
- }, refreshIntervalTimeout)
70
+ }, tokenRefreshInterval)
76
71
  }
77
72
 
78
73
  // Watch for changes in authentication and idle state
@@ -82,8 +77,7 @@ export default defineNuxtPlugin(async () => {
82
77
  [() => keycloak.authenticated, () => idle.value],
83
78
  async ([isAuth, isIdle]) => {
84
79
  if (isAuth) {
85
- // TODO: add storage keys
86
- // sessionStorage.removeItem(ConnectStorageKeys.CONNECT_SESSION_EXPIRED)
80
+ sessionStorage.removeItem(ConnectAuthStorageKey.CONNECT_SESSION_EXPIRED)
87
81
  if (!isIdle) {
88
82
  console.info('[Auth] Starting token refresh schedule.')
89
83
  scheduleRefreshToken()
@@ -11,7 +11,6 @@ export const useConnectAccountStore = defineStore('connect-auth-account-store',
11
11
  const userFirstName = ref<string>(user.value?.firstName || '-')
12
12
  const userLastName = ref<string>(user.value?.lastName || '')
13
13
  const userFullName = computed(() => `${userFirstName.value} ${userLastName.value}`)
14
- const errors = ref<ConnectApiError[]>([])
15
14
 
16
15
  /**
17
16
  * Checks if the current account or the Keycloak user has any of the specified roles.
@@ -42,35 +41,17 @@ export const useConnectAccountStore = defineStore('connect-auth-account-store',
42
41
 
43
42
  /** Get user information from AUTH */
44
43
  async function getAuthUserProfile(identifier: string): Promise<{ firstname: string, lastname: string } | undefined> {
45
- try {
46
- return $authApi<{ firstname: string, lastname: string }>(`/users/${identifier}`, {
47
- parseResponse: JSON.parse,
48
- onResponseError({ response }) {
49
- errors.value.push({
50
- statusCode: response.status || 500,
51
- message: response._data?.message || response._data?.description || 'Error fetching user info.',
52
- detail: response._data.detail || '',
53
- category: ConnectErrorCategory.USER_INFO
54
- })
55
- }
56
- })
57
- } catch (e) {
58
- console.error('Error fetching user info.', e)
59
- // logFetchError(e, 'Error fetching user info.')
60
- }
44
+ return $authApi<{ firstname: string, lastname: string }>(`/users/${identifier}`, {
45
+ parseResponse: JSON.parse
46
+ })
61
47
  }
62
48
 
63
49
  /** Update user information in AUTH with current token info */
64
50
  async function updateAuthUserInfo(): Promise<void> {
65
- try {
66
- await $authApi('/users', {
67
- method: 'POST',
68
- body: { isLogin: true }
69
- })
70
- } catch (e) {
71
- console.error('Error updating auth user info', e)
72
- // logFetchError(e, 'Error updating auth user info')
73
- }
51
+ await $authApi('/users', {
52
+ method: 'POST',
53
+ body: { isLogin: true }
54
+ })
74
55
  }
75
56
 
76
57
  /** Set user name information */
@@ -90,25 +71,9 @@ export const useConnectAccountStore = defineStore('connect-auth-account-store',
90
71
  if (!authUser.value?.keycloakGuid) {
91
72
  return undefined
92
73
  }
93
- try {
94
- // TODO: use orgs fetch instead to get branch name ? $authApi<UserSettings[]>('/users/orgs')
95
- const response = await $authApi<ConnectUserSettings[]>(`/users/${authUser.value.keycloakGuid}/settings`, {
96
- onResponseError({ response }) {
97
- errors.value.push({
98
- statusCode: response.status || 500,
99
- message: response._data?.message || 'Error retrieving user accounts.',
100
- detail: response._data.detail || '',
101
- category: ConnectErrorCategory.ACCOUNT_LIST
102
- })
103
- }
104
- })
105
-
106
- return response?.filter(setting => setting.type === UserSettingsType.ACCOUNT) as ConnectAccount[]
107
- } catch (e) {
108
- console.error('Error retrieving user accounts', e)
109
- // logFetchError(e, 'Error retrieving user accounts')
110
- return undefined
111
- }
74
+ // TODO: use orgs fetch instead to get branch name ? $authApi<UserSettings[]>('/users/orgs')
75
+ const response = await $authApi<ConnectUserSettings[]>(`/users/${authUser.value.keycloakGuid}/settings`)
76
+ return response?.filter(setting => setting.type === UserSettingsType.ACCOUNT) as ConnectAccount[]
112
77
  }
113
78
 
114
79
  /** Set the user account list and current account */
@@ -136,23 +101,8 @@ export const useConnectAccountStore = defineStore('connect-auth-account-store',
136
101
  if (!accountId || !keycloakGuid) {
137
102
  return
138
103
  }
139
- try {
140
- const response = await $authApi<{ count: number }>(`/users/${keycloakGuid}/org/${accountId}/notifications`, {
141
- onResponseError({ response }) {
142
- errors.value.push({
143
- statusCode: response.status || 500,
144
- message: response._data.message || 'Error retrieving pending approvals.',
145
- detail: response._data.detail || '',
146
- category: ConnectErrorCategory.PENDING_APPROVAL_COUNT
147
- })
148
- }
149
- })
150
-
151
- pendingApprovalCount.value = response?.count || 0
152
- } catch (e) {
153
- console.error('Error retrieving pending approvals', e)
154
- // logFetchError(e, 'Error retrieving pending approvals')
155
- }
104
+ const response = await $authApi<{ count: number }>(`/users/${keycloakGuid}/org/${accountId}/notifications`)
105
+ pendingApprovalCount.value = response?.count || 0
156
106
  }
157
107
 
158
108
  async function checkAccountStatus() {
@@ -188,17 +138,21 @@ export const useConnectAccountStore = defineStore('connect-auth-account-store',
188
138
  }
189
139
 
190
140
  async function initAccountStore(): Promise<void> {
191
- await Promise.all([
192
- setAccountInfo(),
193
- updateAuthUserInfo(),
194
- setUserName()
195
- ])
196
-
197
- if (currentAccount.value.id) {
141
+ try {
198
142
  await Promise.all([
199
- checkAccountStatus(),
200
- getPendingApprovalCount()
143
+ setAccountInfo(),
144
+ updateAuthUserInfo(),
145
+ setUserName()
201
146
  ])
147
+
148
+ if (currentAccount.value.id) {
149
+ await Promise.all([
150
+ checkAccountStatus(),
151
+ getPendingApprovalCount()
152
+ ])
153
+ }
154
+ } catch (e) {
155
+ logFetchError(e, '[Account Store] - Error during initialization')
202
156
  }
203
157
  }
204
158
 
@@ -207,7 +161,6 @@ export const useConnectAccountStore = defineStore('connect-auth-account-store',
207
161
  currentAccount.value = {} as ConnectAccount
208
162
  userAccounts.value = []
209
163
  pendingApprovalCount.value = 0
210
- errors.value = []
211
164
  userFirstName.value = user.value?.firstName || '-'
212
165
  userLastName.value = user.value?.lastName || ''
213
166
  }
@@ -217,7 +170,6 @@ export const useConnectAccountStore = defineStore('connect-auth-account-store',
217
170
  currentAccountName,
218
171
  userAccounts,
219
172
  pendingApprovalCount,
220
- errors,
221
173
  userFullName,
222
174
  checkAccountStatus,
223
175
  setUserName,
@@ -0,0 +1,8 @@
1
+ declare module '#app' {
2
+ interface PageMeta {
3
+ onBeforeSessionExpired?: () => void | Promise<void>
4
+ onAccountChange?: (oldAccount: ConnectAccount, newAccount: ConnectAccount) => boolean
5
+ }
6
+ }
7
+
8
+ export {}
@@ -0,0 +1,33 @@
1
+ import type { Pinia, Store } from 'pinia'
2
+ import { getActivePinia } from 'pinia'
3
+
4
+ interface ExtendedPinia extends Pinia {
5
+ _s: Map<string, Store>
6
+ }
7
+
8
+ /**
9
+ * Resets specific Pinia stores if store IDs are specified.
10
+ * If no IDs are provided, resets all stores that have a `$reset` method.
11
+ * @param {string[]} storeIds - Array of store IDs to reset. If empty, all stores will be reset.
12
+ */
13
+ export function resetPiniaStores(storeIds: string[] = []): void {
14
+ const pinia = getActivePinia() as ExtendedPinia
15
+
16
+ if (!pinia && import.meta.env.DEV === true) {
17
+ console.warn('No pinia stores found.')
18
+ return
19
+ }
20
+
21
+ // null check still fails so must catch error instead
22
+ pinia._s.forEach((store) => {
23
+ if (storeIds.length === 0 || storeIds.includes(store.$id)) {
24
+ try {
25
+ store.$reset()
26
+ } catch {
27
+ if (import.meta.env.DEV === true) {
28
+ console.warn(`Store "${store.$id}" does not implement $reset. Skipping reset.`)
29
+ }
30
+ }
31
+ }
32
+ })
33
+ }
@@ -0,0 +1,23 @@
1
+ // cant use await inside definePageMeta so must use separate util to set page meta if await required
2
+ /**
3
+ * Sets the `onBeforeSessionExpired` callback for the current route.
4
+ *
5
+ * This function allows you to provide a callback (`cb`) that will be called before the session expires.
6
+ * The callback can either be synchronous (returning `void`) or asynchronous (returning a `Promise`).
7
+ *
8
+ * @param {() => T | Promise<T>} cb - The callback function to execute before session expiry.
9
+ *
10
+ * @example
11
+ * setOnBeforeSessionExpired(() => {
12
+ * // Do something before session expiry
13
+ * });
14
+ *
15
+ * @example
16
+ * setOnBeforeSessionExpired(() => submitApplication());
17
+ */
18
+ export function setOnBeforeSessionExpired<T>(cb: () => T | Promise<T>) {
19
+ const route = useRoute()
20
+ route.meta.onBeforeSessionExpired = async () => {
21
+ await cb()
22
+ }
23
+ }
@@ -21,6 +21,15 @@ export default {
21
21
  teamMembers: 'Team Members',
22
22
  transactions: 'Transactions'
23
23
  },
24
+ sessionExpiry: {
25
+ title: 'Session Expiring Soon',
26
+ content: 'Your session is about to expire due to inactivity. You will be logged out in {boldStart}0{boldEnd} seconds. Press any key to continue your session. | Your session is about to expire due to inactivity. You will be logged out in {boldStart}1{boldEnd} second. Press any key to continue your session. | Your session is about to expire due to inactivity. You will be logged out in {boldStart}{count}{boldEnd} seconds. Press any key to continue your session.',
27
+ sessionExpired: 'Session Expired',
28
+ continueBtn: {
29
+ main: 'Continue Session',
30
+ aria: 'Your session is about to expire, press any key to continue your session.'
31
+ }
32
+ },
24
33
  text: {
25
34
  notifications: {
26
35
  none: 'No Notifications',
package/nuxt.config.ts CHANGED
@@ -71,7 +71,11 @@ export default defineNuxtConfig({
71
71
  authApiUrl: '',
72
72
  authApiVersion: '',
73
73
  xApiKey: '',
74
- authWebUrl: ''
74
+ authWebUrl: '',
75
+ tokenRefreshInterval: '',
76
+ tokenMinValidity: '',
77
+ sessionInactivityTimeout: '',
78
+ sessionModalTimeout: ''
75
79
  }
76
80
  }
77
81
  })
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sbc-connect/nuxt-auth",
3
3
  "type": "module",
4
- "version": "0.1.7",
4
+ "version": "0.1.8",
5
5
  "repository": "github:bcgov/connect-nuxt",
6
6
  "license": "BSD-3-Clause",
7
7
  "main": "./nuxt.config.ts",
@@ -13,7 +13,7 @@
13
13
  "typescript": "^5.8.3",
14
14
  "vue-tsc": "^3.0.4",
15
15
  "@sbc-connect/eslint-config": "0.0.6",
16
- "@sbc-connect/playwright-config": "0.0.5",
16
+ "@sbc-connect/playwright-config": "0.0.6",
17
17
  "@sbc-connect/vitest-config": "0.0.6"
18
18
  },
19
19
  "dependencies": {
@@ -21,7 +21,7 @@
21
21
  "keycloak-js": "^26.2.0",
22
22
  "pinia": "^3.0.3",
23
23
  "pinia-plugin-persistedstate": "^4.4.1",
24
- "@sbc-connect/nuxt-base": "0.1.7"
24
+ "@sbc-connect/nuxt-base": "0.1.8"
25
25
  },
26
26
  "scripts": {
27
27
  "preinstall": "npx only-allow pnpm",
@@ -1,6 +0,0 @@
1
- export enum ConnectErrorCategory {
2
- USER_ACCOUNTS = 'user-accounts',
3
- ACCOUNT_LIST = 'account-list',
4
- PENDING_APPROVAL_COUNT = 'pending-approval-count',
5
- USER_INFO = 'user-info'
6
- }
@@ -1,6 +0,0 @@
1
- export interface ConnectApiError {
2
- category: ConnectErrorCategory
3
- detail?: string | string[]
4
- message: string
5
- statusCode: number
6
- }