@sbc-connect/nuxt-auth 0.7.0 → 0.8.0
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/CHANGELOG.md +19 -0
- package/app/components/Connect/Modal/SessionExpired.vue +15 -1
- package/app/composables/useConnectAccountFlowRedirect.ts +3 -1
- package/app/composables/useConnectAuth.ts +1 -3
- package/app/composables/useConnectHeaderOptions.ts +11 -7
- package/app/plugins/connect-auth.client.ts +15 -2
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @sbc-connect/nuxt-auth
|
|
2
2
|
|
|
3
|
+
## 0.8.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#140](https://github.com/bcgov/connect-nuxt/pull/140) [`7704fda`](https://github.com/bcgov/connect-nuxt/commit/7704fda8e016b67d0928964652e94f0bb792ff6c) Thanks [@cameron-eyds](https://github.com/cameron-eyds)! - Param preservation in expiry logout and header login
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [[`83c5627`](https://github.com/bcgov/connect-nuxt/commit/83c5627689a50d6fa27f12f2ab7b2ef65e752d0e)]:
|
|
12
|
+
- @sbc-connect/nuxt-forms@0.6.2
|
|
13
|
+
|
|
14
|
+
## 0.7.1
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- Updated dependencies [[`e6dc55d`](https://github.com/bcgov/connect-nuxt/commit/e6dc55d18b84754e2416112ebbf42b780b8bb3ac)]:
|
|
19
|
+
- @sbc-connect/nuxt-base@0.7.1
|
|
20
|
+
- @sbc-connect/nuxt-forms@0.6.1
|
|
21
|
+
|
|
3
22
|
## 0.7.0
|
|
4
23
|
|
|
5
24
|
### Minor Changes
|
|
@@ -4,11 +4,25 @@ const rtc = useRuntimeConfig().public
|
|
|
4
4
|
const modalTimeout = rtc.sessionModalTimeout ? Number(rtc.sessionModalTimeout) : 120000
|
|
5
5
|
const { t } = useI18n()
|
|
6
6
|
const route = useRoute()
|
|
7
|
+
const localePath = useLocalePath()
|
|
7
8
|
|
|
8
9
|
const emit = defineEmits<{ close: [] }>()
|
|
9
10
|
|
|
10
11
|
const timeRemaining = ref(toValue((modalTimeout) / 1000))
|
|
11
12
|
|
|
13
|
+
// Capture the user's current location when the modal opens, before any potential URL changes
|
|
14
|
+
const capturedUrl = `${window.location.origin}${route.fullPath}`
|
|
15
|
+
|
|
16
|
+
/** Build a login page URL with a return param preserving the user's current location.
|
|
17
|
+
* The return value is double-encoded so it survives the Keycloak redirect decode
|
|
18
|
+
* without the original query params bleeding into the login page's query string.
|
|
19
|
+
*/
|
|
20
|
+
function getSessionExpiredRedirect(): string {
|
|
21
|
+
const loginPath = localePath('/auth/login')
|
|
22
|
+
const returnUrl = encodeURIComponent(encodeURIComponent(capturedUrl))
|
|
23
|
+
return `${window.location.origin}${loginPath}?return=${returnUrl}`
|
|
24
|
+
}
|
|
25
|
+
|
|
12
26
|
const intervalId = setInterval(async () => {
|
|
13
27
|
const value = timeRemaining.value - 1
|
|
14
28
|
timeRemaining.value = value < 0 ? 0 : value
|
|
@@ -18,7 +32,7 @@ const intervalId = setInterval(async () => {
|
|
|
18
32
|
await route.meta.onBeforeSessionExpired()
|
|
19
33
|
}
|
|
20
34
|
sessionStorage.setItem(ConnectAuthStorageKey.CONNECT_SESSION_EXPIRED, 'true')
|
|
21
|
-
await useConnectAuth().logout()
|
|
35
|
+
await useConnectAuth().logout(getSessionExpiredRedirect())
|
|
22
36
|
}
|
|
23
37
|
}, 1000)
|
|
24
38
|
|
|
@@ -6,7 +6,9 @@ export const useConnectAccountFlowRedirect = () => {
|
|
|
6
6
|
const { currentAccount, userAccounts } = useConnectAccountStore()
|
|
7
7
|
const localePath = useLocalePath()
|
|
8
8
|
const ac = useAppConfig().connect.login
|
|
9
|
-
const externalRedirectUrl = route.query.return
|
|
9
|
+
const externalRedirectUrl = route.query.return
|
|
10
|
+
? decodeURIComponent(route.query.return as string)
|
|
11
|
+
: undefined
|
|
10
12
|
const internalRedirectUrl = ac.redirect
|
|
11
13
|
const query = { ...route.query }
|
|
12
14
|
query.accountid = String(currentAccount.id)
|
|
@@ -30,9 +30,7 @@ export const useConnectAuth = () => {
|
|
|
30
30
|
|
|
31
31
|
if (siteminderUrl) {
|
|
32
32
|
const cleanedUri = redirectUri.replace(/(https?:\/\/)|(\/)+/g, '$1$2')
|
|
33
|
-
|
|
34
|
-
const returlValue = queryString ? `${cleanedUri}${queryString}` : cleanedUri
|
|
35
|
-
redirectUri = `${siteminderUrl}?returl=${returlValue}&retnow=1`
|
|
33
|
+
redirectUri = `${siteminderUrl}?returl=${encodeURIComponent(cleanedUri)}&retnow=1`
|
|
36
34
|
}
|
|
37
35
|
|
|
38
36
|
resetPiniaStores()
|
|
@@ -10,7 +10,7 @@ export function useConnectHeaderOptions() {
|
|
|
10
10
|
const ac = useAppConfig().connect
|
|
11
11
|
const route = useRoute()
|
|
12
12
|
const overlay = useOverlay()
|
|
13
|
-
const { t
|
|
13
|
+
const { t } = useNuxtApp().$i18n
|
|
14
14
|
const { login, isAuthenticated, authUser } = useConnectAuth()
|
|
15
15
|
const accountStore = useConnectAccountStore()
|
|
16
16
|
const localePath = useLocalePath()
|
|
@@ -135,9 +135,13 @@ export function useConnectHeaderOptions() {
|
|
|
135
135
|
return options
|
|
136
136
|
})
|
|
137
137
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
138
|
+
// Build a redirect URL through the login page so the connect-auth middleware
|
|
139
|
+
// processes query params (e.g. 'return') after Keycloak authentication
|
|
140
|
+
function getLoginRedirectUrl(): string {
|
|
141
|
+
const loginPath = localePath('/auth/login')
|
|
142
|
+
const queryString = window.location.search
|
|
143
|
+
return `${appBaseUrl}${loginPath.startsWith('/') ? loginPath.slice(1) : loginPath}${queryString}`
|
|
144
|
+
}
|
|
141
145
|
|
|
142
146
|
const loginOptionsMap: Record<'bcsc' | 'bceid' | 'idir',
|
|
143
147
|
{ label: string, icon: string, onSelect: () => Promise<void> }
|
|
@@ -145,17 +149,17 @@ export function useConnectHeaderOptions() {
|
|
|
145
149
|
bcsc: {
|
|
146
150
|
label: t('connect.label.bcsc'),
|
|
147
151
|
icon: 'i-mdi-account-card-details-outline',
|
|
148
|
-
onSelect: () => login(ConnectIdpHint.BCSC,
|
|
152
|
+
onSelect: () => login(ConnectIdpHint.BCSC, getLoginRedirectUrl())
|
|
149
153
|
},
|
|
150
154
|
bceid: {
|
|
151
155
|
label: t('connect.label.bceid'),
|
|
152
156
|
icon: 'i-mdi-two-factor-authentication',
|
|
153
|
-
onSelect: () => login(ConnectIdpHint.BCEID,
|
|
157
|
+
onSelect: () => login(ConnectIdpHint.BCEID, getLoginRedirectUrl())
|
|
154
158
|
},
|
|
155
159
|
idir: {
|
|
156
160
|
label: t('connect.label.idir'),
|
|
157
161
|
icon: 'i-mdi-account-group-outline',
|
|
158
|
-
onSelect: () => login(ConnectIdpHint.IDIR,
|
|
162
|
+
onSelect: () => login(ConnectIdpHint.IDIR, getLoginRedirectUrl())
|
|
159
163
|
}
|
|
160
164
|
}
|
|
161
165
|
|
|
@@ -15,6 +15,19 @@ export default defineNuxtPlugin(async () => {
|
|
|
15
15
|
const tokenMinValidity = rtc.tokenMinValidity ? Number(rtc.tokenMinValidity) / 1000 : 120
|
|
16
16
|
const sessionInactivityTimeout = rtc.sessionInactivityTimeout ? Number(rtc.sessionInactivityTimeout) : 1800000
|
|
17
17
|
|
|
18
|
+
// Logout via Keycloak, redirecting to the login page with a return param preserving the user's current URL.
|
|
19
|
+
// The return value is double-encoded so it survives the Keycloak redirect decode
|
|
20
|
+
// without the original query params bleeding into the login page's query string.
|
|
21
|
+
function logoutWithReturn(kc: Keycloak) {
|
|
22
|
+
const localePath = useLocalePath()
|
|
23
|
+
const route = useRoute()
|
|
24
|
+
const loginPath = localePath('/auth/login')
|
|
25
|
+
const currentUrl = `${window.location.origin}${route.fullPath}`
|
|
26
|
+
const returnUrl = encodeURIComponent(encodeURIComponent(currentUrl))
|
|
27
|
+
const redirectUri = `${window.location.origin}${loginPath}?return=${returnUrl}`
|
|
28
|
+
kc.logout({ redirectUri })
|
|
29
|
+
}
|
|
30
|
+
|
|
18
31
|
try {
|
|
19
32
|
// default behaviour when keycloak session expires
|
|
20
33
|
// try to update token - log out if token update fails
|
|
@@ -27,7 +40,7 @@ export default defineNuxtPlugin(async () => {
|
|
|
27
40
|
console.info('[Auth] Token refreshed.')
|
|
28
41
|
} catch (error) {
|
|
29
42
|
console.error('[Auth] Failed to refresh token on expiration; logging out.', error)
|
|
30
|
-
keycloak
|
|
43
|
+
logoutWithReturn(keycloak)
|
|
31
44
|
}
|
|
32
45
|
}
|
|
33
46
|
|
|
@@ -62,7 +75,7 @@ export default defineNuxtPlugin(async () => {
|
|
|
62
75
|
console.info('[Auth] Token refreshed.')
|
|
63
76
|
} catch (error) {
|
|
64
77
|
console.error('[Auth] Failed to refresh token; logging out.', error)
|
|
65
|
-
keycloak
|
|
78
|
+
logoutWithReturn(keycloak)
|
|
66
79
|
}
|
|
67
80
|
}
|
|
68
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.
|
|
4
|
+
"version": "0.8.0",
|
|
5
5
|
"repository": "github:bcgov/connect-nuxt",
|
|
6
6
|
"license": "BSD-3-Clause",
|
|
7
7
|
"main": "./nuxt.config.ts",
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
"keycloak-js": "26.2.2",
|
|
23
23
|
"pinia": "3.0.4",
|
|
24
24
|
"pinia-plugin-persistedstate": "4.7.1",
|
|
25
|
-
"@sbc-connect/nuxt-base": "0.7.
|
|
26
|
-
"@sbc-connect/nuxt-forms": "0.6.
|
|
25
|
+
"@sbc-connect/nuxt-base": "0.7.1",
|
|
26
|
+
"@sbc-connect/nuxt-forms": "0.6.2"
|
|
27
27
|
},
|
|
28
28
|
"scripts": {
|
|
29
29
|
"preinstall": "npx only-allow pnpm",
|