@sbc-connect/nuxt-auth 0.4.1 → 0.5.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 +6 -0
- package/app/composables/useAuthApi.ts +5 -4
- package/app/composables/useConnectAccountFlowRedirect.ts +25 -10
- package/app/middleware/connect-auth.ts +17 -11
- package/app/pages/auth/login.vue +2 -1
- package/app/pages/auth/terms-of-use.vue +1 -1
- package/app/stores/connect-account.ts +16 -8
- package/package.json +1 -1
- package/testMocks/auth/index.ts +1 -0
- package/testMocks/auth/profile/index.ts +9 -0
- package/testMocks/auth/profile/json/PUBLIC_USER.json +29 -0
- package/testMocks/mock-helpers.ts +4 -1
- package/app/middleware/connect-login-page.ts +0 -26
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @sbc-connect/nuxt-auth
|
|
2
2
|
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#123](https://github.com/bcgov/connect-nuxt/pull/123) [`3c33132`](https://github.com/bcgov/connect-nuxt/commit/3c331327a27cfbd0613c268abf97842288d10f8c) Thanks [@cameron-eyds](https://github.com/cameron-eyds)! - Auth Redirect Enhancements
|
|
8
|
+
|
|
3
9
|
## 0.4.1
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import type { ConnectCreateAccount } from '#auth/app/interfaces/connect-account'
|
|
2
|
-
|
|
3
1
|
export const useAuthApi = () => {
|
|
4
2
|
const { $authApi } = useNuxtApp()
|
|
5
3
|
const queryCache = useQueryCache()
|
|
@@ -34,7 +32,10 @@ export const useAuthApi = () => {
|
|
|
34
32
|
*/
|
|
35
33
|
const useCreateAccount = defineMutation(() => {
|
|
36
34
|
const { mutateAsync, ...mutation } = useMutation({
|
|
37
|
-
mutation: (vars: {
|
|
35
|
+
mutation: (vars: {
|
|
36
|
+
payload: ConnectCreateAccount
|
|
37
|
+
successCb?: (createRes: ConnectAuthProfile) => Promise<unknown>
|
|
38
|
+
}) => {
|
|
38
39
|
return $authApi<ConnectAuthProfile>('/orgs', {
|
|
39
40
|
method: 'POST',
|
|
40
41
|
body: vars.payload
|
|
@@ -48,7 +49,7 @@ export const useAuthApi = () => {
|
|
|
48
49
|
onSuccess: async (_, _vars) => {
|
|
49
50
|
await queryCache.invalidateQueries({ key: ['auth-user-profile'], exact: true })
|
|
50
51
|
if (_vars.successCb) {
|
|
51
|
-
await _vars.successCb()
|
|
52
|
+
await _vars.successCb(_)
|
|
52
53
|
}
|
|
53
54
|
}
|
|
54
55
|
})
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { RouteLocationNormalizedGeneric } from '#vue-router'
|
|
2
2
|
|
|
3
3
|
export const useConnectAccountFlowRedirect = () => {
|
|
4
|
-
function finalRedirect(route: RouteLocationNormalizedGeneric) {
|
|
4
|
+
function finalRedirect(route: RouteLocationNormalizedGeneric, manageAccount = false) {
|
|
5
|
+
const { authUser } = useConnectAuth()
|
|
6
|
+
const { userAccounts } = useConnectAccountStore()
|
|
5
7
|
const localePath = useLocalePath()
|
|
6
8
|
const ac = useAppConfig().connect.login
|
|
7
9
|
const externalRedirectUrl = route.query.return as string | undefined
|
|
@@ -9,24 +11,37 @@ export const useConnectAccountFlowRedirect = () => {
|
|
|
9
11
|
|
|
10
12
|
const query = { ...route.query }
|
|
11
13
|
|
|
12
|
-
if (
|
|
13
|
-
|
|
14
|
+
if (manageAccount && userAccounts.length !== 1) {
|
|
15
|
+
const isBcscCreate
|
|
16
|
+
= userAccounts.length === 0
|
|
17
|
+
&& authUser.value.loginSource === ConnectLoginSource.BCSC
|
|
18
|
+
|
|
19
|
+
return isBcscCreate
|
|
20
|
+
? navigateTo({ path: localePath('/auth/account/create'), query })
|
|
21
|
+
: navigateTo({ path: localePath('/auth/account/select'), query })
|
|
14
22
|
}
|
|
15
23
|
|
|
16
24
|
if (externalRedirectUrl) {
|
|
25
|
+
const cleanQuery = { ...query }
|
|
26
|
+
delete cleanQuery.return
|
|
27
|
+
|
|
17
28
|
return navigateTo(
|
|
18
29
|
{
|
|
19
|
-
path: appendUrlParam(
|
|
20
|
-
|
|
30
|
+
path: appendUrlParam(
|
|
31
|
+
externalRedirectUrl,
|
|
32
|
+
'accountid',
|
|
33
|
+
useConnectAccountStore().currentAccount.id
|
|
34
|
+
),
|
|
35
|
+
query: cleanQuery
|
|
21
36
|
},
|
|
22
37
|
{ external: true }
|
|
23
38
|
)
|
|
24
|
-
} else {
|
|
25
|
-
return navigateTo({
|
|
26
|
-
path: localePath(internalRedirectUrl),
|
|
27
|
-
query
|
|
28
|
-
})
|
|
29
39
|
}
|
|
40
|
+
|
|
41
|
+
return navigateTo({
|
|
42
|
+
path: localePath(internalRedirectUrl),
|
|
43
|
+
query
|
|
44
|
+
})
|
|
30
45
|
}
|
|
31
46
|
|
|
32
47
|
return {
|
|
@@ -5,27 +5,33 @@ export default defineNuxtRouteMiddleware(async (to) => {
|
|
|
5
5
|
const authApi = useAuthApi()
|
|
6
6
|
const { finalRedirect } = useConnectAccountFlowRedirect()
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
const isLoginPage = to.meta.connectLogin === true
|
|
9
|
+
const isTosPage = to.meta.connectTosPage === true
|
|
10
|
+
|
|
11
|
+
if (!isAuthenticated.value && !isLoginPage && !rtc.playwright) {
|
|
12
|
+
const defaultReturn = `${rtc.baseUrl}${to.fullPath.slice(1)}`
|
|
13
|
+
const returnUrl = (to.query.return && String(to.query.return)) || defaultReturn
|
|
14
|
+
|
|
15
|
+
return navigateTo(
|
|
16
|
+
{
|
|
17
|
+
path: localePath('/auth/login'),
|
|
18
|
+
query: {
|
|
19
|
+
...to.query,
|
|
20
|
+
return: returnUrl
|
|
21
|
+
}
|
|
15
22
|
}
|
|
16
|
-
|
|
23
|
+
)
|
|
17
24
|
}
|
|
18
25
|
|
|
19
26
|
if (isAuthenticated.value) {
|
|
20
27
|
const { data, refresh } = await authApi.getAuthUserProfile()
|
|
21
28
|
await refresh()
|
|
22
29
|
const hasAccepted = data.value?.userTerms.isTermsOfUseAccepted
|
|
23
|
-
const isTosPage = to.meta.connectTosPage === true
|
|
24
30
|
if (!hasAccepted && !isTosPage) {
|
|
25
31
|
const query = { ...to.query }
|
|
26
32
|
return navigateTo({ path: localePath('/auth/terms-of-use'), query })
|
|
27
|
-
} else if (hasAccepted && isTosPage) {
|
|
28
|
-
return finalRedirect(to)
|
|
33
|
+
} else if (hasAccepted && (isTosPage || isLoginPage)) {
|
|
34
|
+
return finalRedirect(to, true)
|
|
29
35
|
}
|
|
30
36
|
}
|
|
31
37
|
|
package/app/pages/auth/login.vue
CHANGED
|
@@ -12,7 +12,8 @@ useHead({
|
|
|
12
12
|
definePageMeta({
|
|
13
13
|
layout: 'connect-auth',
|
|
14
14
|
hideBreadcrumbs: true,
|
|
15
|
-
middleware: 'connect-
|
|
15
|
+
middleware: 'connect-auth',
|
|
16
|
+
connectLogin: true
|
|
16
17
|
})
|
|
17
18
|
|
|
18
19
|
const isSessionExpired = sessionStorage.getItem(ConnectAuthStorageKey.CONNECT_SESSION_EXPIRED)
|
|
@@ -70,7 +70,7 @@ const disableButtons = computed<boolean>(() => {
|
|
|
70
70
|
@submit="patchTermsOfUse({
|
|
71
71
|
accepted: true,
|
|
72
72
|
version: data!.versionId,
|
|
73
|
-
successCb: async () => await finalRedirect(useRoute()),
|
|
73
|
+
successCb: async () => await finalRedirect(useRoute(), true),
|
|
74
74
|
})"
|
|
75
75
|
/>
|
|
76
76
|
</UContainer>
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { getAccountCreateSchema } from '#auth/app/utils/schemas/account'
|
|
2
2
|
import type { AccountProfileSchema } from '#auth/app/utils/schemas/account'
|
|
3
|
-
import type { ConnectCreateAccount } from '#auth/app/interfaces/connect-account'
|
|
4
3
|
|
|
5
4
|
export const useConnectAccountStore = defineStore('connect-auth-account-store', () => {
|
|
6
5
|
const { $authApi } = useNuxtApp()
|
|
@@ -88,13 +87,22 @@ export const useConnectAccountStore = defineStore('connect-auth-account-store',
|
|
|
88
87
|
await createAccount({
|
|
89
88
|
payload,
|
|
90
89
|
// Update User Contact Info on create account success
|
|
91
|
-
successCb: async () =>
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
90
|
+
successCb: async (createResponse: ConnectAuthProfile) => {
|
|
91
|
+
// Refresh and switch to new account prior to redirect
|
|
92
|
+
if (createResponse?.id) {
|
|
93
|
+
await setAccountInfo()
|
|
94
|
+
switchCurrentAccount(createResponse.id)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Update user contact and then redirect regardless of success or failure
|
|
98
|
+
await updateUserContact({
|
|
99
|
+
email: accountFormState.emailAddress,
|
|
100
|
+
phone: accountFormState.phone.phoneNumber,
|
|
101
|
+
phoneExtension: accountFormState.phone.ext,
|
|
102
|
+
successCb: async () => await finalRedirect(useRoute()),
|
|
103
|
+
errorCb: async () => await finalRedirect(useRoute())
|
|
104
|
+
})
|
|
105
|
+
}
|
|
98
106
|
})
|
|
99
107
|
} catch (error) {
|
|
100
108
|
// Error handled in useAuthApi
|
package/package.json
CHANGED
package/testMocks/auth/index.ts
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import { createResolver } from 'nuxt/kit'
|
|
3
|
+
|
|
4
|
+
const { resolve } = createResolver(import.meta.url)
|
|
5
|
+
|
|
6
|
+
export const getUserProfileMock = () => {
|
|
7
|
+
const json: ConnectAuthProfile = JSON.parse(fs.readFileSync(resolve('./json/PUBLIC_USER.json'), 'utf8'))
|
|
8
|
+
return json
|
|
9
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"contacts": [
|
|
3
|
+
{
|
|
4
|
+
"email": "v9x3z!q__mock@fuzzmail.zzz",
|
|
5
|
+
"phone": "(902) 555-xyzz",
|
|
6
|
+
"phoneExtension": "7q1"
|
|
7
|
+
}
|
|
8
|
+
],
|
|
9
|
+
"created": "2024-03-19T04:92:17+00:00",
|
|
10
|
+
"firstname": "Nxlqrp3##",
|
|
11
|
+
"id": 88422,
|
|
12
|
+
"idpUserid": "ZZ91XQWPMR7T4!$CBA9900PLMNXXQTT",
|
|
13
|
+
"keycloakGuid": "xxxxxxxx-bb77-49c1-f01a-xxxxxxxxx",
|
|
14
|
+
"lastname": "T'W&&two--",
|
|
15
|
+
"loginSource": "BCSC",
|
|
16
|
+
"loginTime": "2026-01-23T28:77:59+00:00",
|
|
17
|
+
"modified": "2026-01-23T28:77:59+00:00",
|
|
18
|
+
"modifiedBy": "Nxlqrp3## T'W&&two--",
|
|
19
|
+
"type": "PUBLIC_USER",
|
|
20
|
+
"userStatus": 1,
|
|
21
|
+
"userTerms": {
|
|
22
|
+
"isTermsOfUseAccepted": true,
|
|
23
|
+
"termsOfUseAcceptedVersion": "999x"
|
|
24
|
+
},
|
|
25
|
+
"username": "bcsc/zz91xqwpmr7t4!$cba9900plmnxxqtt",
|
|
26
|
+
"verified": true,
|
|
27
|
+
"version": 9999
|
|
28
|
+
}
|
|
29
|
+
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import type { Page } from '@playwright/test'
|
|
2
2
|
import { AccountType } from '#auth/app/enums/account-type'
|
|
3
|
-
import { getUserSettingsMock } from '#auth/testMocks/auth'
|
|
3
|
+
import { getUserProfileMock, getUserSettingsMock } from '#auth/testMocks/auth'
|
|
4
4
|
|
|
5
5
|
export const mockApiCallsForSetAccount = async (
|
|
6
6
|
page: Page,
|
|
7
7
|
accountType: AccountType = AccountType.PREMIUM
|
|
8
8
|
) => {
|
|
9
|
+
page.route('**/users/@me', async (route) => {
|
|
10
|
+
await route.fulfill({ json: getUserProfileMock() })
|
|
11
|
+
})
|
|
9
12
|
page.route('**/users/**/settings', async (route) => {
|
|
10
13
|
await route.fulfill({ json: getUserSettingsMock(accountType) })
|
|
11
14
|
})
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
export default defineNuxtRouteMiddleware((to) => {
|
|
2
|
-
const { isAuthenticated, authUser } = useConnectAuth()
|
|
3
|
-
const accountStore = useConnectAccountStore()
|
|
4
|
-
const localePath = useLocalePath()
|
|
5
|
-
const { finalRedirect } = useConnectAccountFlowRedirect()
|
|
6
|
-
|
|
7
|
-
const numAccounts = accountStore.userAccounts.length
|
|
8
|
-
|
|
9
|
-
if (isAuthenticated.value) {
|
|
10
|
-
if (numAccounts === 1) {
|
|
11
|
-
return finalRedirect(to)
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
if (numAccounts === 0 && authUser.value.loginSource === ConnectLoginSource.BCSC) {
|
|
15
|
-
return navigateTo({
|
|
16
|
-
path: localePath('/auth/account/create'),
|
|
17
|
-
query: to.query
|
|
18
|
-
})
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return navigateTo({
|
|
22
|
-
path: localePath('/auth/account/select'),
|
|
23
|
-
query: to.query
|
|
24
|
-
})
|
|
25
|
-
}
|
|
26
|
-
})
|