adata-ui 2.1.40-beta.6 → 2.1.40-beta.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/components/forms/input/AInputV2.vue +3 -3
- package/components/modals/id/IdConfirmAccountOtpModal.vue +4 -9
- package/components/modals/id/IdLoginModal.vue +3 -8
- package/components/modals/id/IdModals.vue +23 -10
- package/components/modals/id/IdTwoFactorModal.vue +3 -8
- package/composables/useAccessTokenCookie.ts +25 -0
- package/package.json +1 -1
|
@@ -262,7 +262,7 @@ const layoutClass = computed(() => {
|
|
|
262
262
|
|
|
263
263
|
const iconOffsetClass = computed(() => {
|
|
264
264
|
if (!props.label) return ''
|
|
265
|
-
return props.size === 'md' ? '-translate-y-[
|
|
265
|
+
return props.size === 'md' ? '-translate-y-[7px]' : '-translate-y-[5px]'
|
|
266
266
|
})
|
|
267
267
|
|
|
268
268
|
const showClearButton = computed(() =>
|
|
@@ -308,11 +308,11 @@ function rowDisplay(item: T): unknown {
|
|
|
308
308
|
>
|
|
309
309
|
<div
|
|
310
310
|
ref="fieldRef"
|
|
311
|
-
class="input-v2__field text-deepblue-900 relative flex w-full cursor-text items-center gap-2 rounded-[10px] border border-solid
|
|
311
|
+
class="input-v2__field text-deepblue-900 relative flex w-full cursor-text items-center gap-2 rounded-[10px] border border-solid transition-colors duration-200 focus-within:border-blue-600 focus-within:ring-2 focus-within:ring-blue-600/20 hover:border-blue-500 dark:text-gray-200 dark:focus-within:border-blue-400 dark:focus-within:ring-blue-400/20 dark:hover:border-blue-400"
|
|
312
312
|
:class="[
|
|
313
313
|
layoutClass,
|
|
314
314
|
fieldClass,
|
|
315
|
-
startIcon ? 'pl-3' : '',
|
|
315
|
+
startIcon ? 'pl-3' : 'pl-4',
|
|
316
316
|
clearable || endIcon || loading || $slots.endButton ? 'pr-3' : 'pr-4',
|
|
317
317
|
{ 'input-v2__field--error': hasError, 'input-v2__field--disabled': disabled },
|
|
318
318
|
]"
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { removeTrailingSlash } from '#adata-ui/components/utils/removeTrailingSlash'
|
|
3
2
|
import IdOtpInput from '#adata-ui/components/modals/id/IdOtpInput.vue'
|
|
3
|
+
import { removeTrailingSlash } from '#adata-ui/components/utils/removeTrailingSlash'
|
|
4
|
+
import { writeAccessTokenCookie } from '#adata-ui/composables/useAccessTokenCookie'
|
|
4
5
|
|
|
5
6
|
const { $toast } = useNuxtApp()
|
|
6
7
|
const { t, locale } = useI18n()
|
|
@@ -40,17 +41,11 @@ async function loginUser() {
|
|
|
40
41
|
},
|
|
41
42
|
})
|
|
42
43
|
const { data: cookiesData } = await response.json()
|
|
43
|
-
if (cookiesData) {
|
|
44
|
+
if (cookiesData?.access_token) {
|
|
44
45
|
const { access_token, expire_in } = cookiesData
|
|
45
|
-
const hostname = location.hostname.split('.').reverse()
|
|
46
46
|
useCookie('autoLogout').value = true
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
maxAge: expire_in,
|
|
50
|
-
domain: `.${hostname[1]}.${hostname[0]}`,
|
|
51
|
-
path: '/',
|
|
52
|
-
secure: true,
|
|
53
|
-
}).value = access_token
|
|
48
|
+
await writeAccessTokenCookie(access_token, expire_in)
|
|
54
49
|
}
|
|
55
50
|
}
|
|
56
51
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { removeTrailingSlash } from '#adata-ui/components/utils/removeTrailingSlash'
|
|
3
|
+
import { writeAccessTokenCookie } from '#adata-ui/composables/useAccessTokenCookie'
|
|
3
4
|
import { navigateToLocalizedPage } from '#adata-ui/utils/localizedNavigation'
|
|
4
5
|
import * as z from 'zod'
|
|
5
6
|
import { useUrls } from '#adata-ui/composables/useUrls'
|
|
@@ -127,17 +128,11 @@ async function submit() {
|
|
|
127
128
|
},
|
|
128
129
|
})
|
|
129
130
|
const { data: cookiesData } = await response.json()
|
|
130
|
-
if (cookiesData) {
|
|
131
|
+
if (cookiesData?.access_token) {
|
|
131
132
|
const { access_token, expire_in } = cookiesData
|
|
132
|
-
const hostname = location.hostname.split('.').reverse()
|
|
133
133
|
useCookie('autoLogout').value = true
|
|
134
134
|
|
|
135
|
-
|
|
136
|
-
maxAge: expire_in,
|
|
137
|
-
domain: COOKIE_DOMAIN,
|
|
138
|
-
path: '/',
|
|
139
|
-
secure: true,
|
|
140
|
-
}).value = access_token
|
|
135
|
+
await writeAccessTokenCookie(access_token, expire_in)
|
|
141
136
|
}
|
|
142
137
|
$toast.success(t('reuse.successfully'))
|
|
143
138
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import
|
|
2
|
+
import IdAutoLogoutModal from '#adata-ui/components/modals/id/IdAutoLogoutModal.vue'
|
|
3
3
|
import IdConfirmAccountOtpModal from '#adata-ui/components/modals/id/IdConfirmAccountOtpModal.vue'
|
|
4
4
|
import IdConfirmSuccessfulModal from '#adata-ui/components/modals/id/IdConfirmSuccessfulModal.vue'
|
|
5
5
|
import IdLoginModal from '#adata-ui/components/modals/id/IdLoginModal.vue'
|
|
@@ -9,7 +9,8 @@ import IdRecoveryModal from '#adata-ui/components/modals/id/IdRecoveryModal.vue'
|
|
|
9
9
|
import IdRegistrationModal from '#adata-ui/components/modals/id/IdRegistrationModal.vue'
|
|
10
10
|
import IdResetPasswordOtpModal from '#adata-ui/components/modals/id/IdResetPasswordOtpModal.vue'
|
|
11
11
|
import IdTwoFactorModal from '#adata-ui/components/modals/id/IdTwoFactorModal.vue'
|
|
12
|
-
import
|
|
12
|
+
import { removeTrailingSlash } from '#adata-ui/components/utils/removeTrailingSlash'
|
|
13
|
+
import { writeAccessTokenCookie } from '#adata-ui/composables/useAccessTokenCookie'
|
|
13
14
|
|
|
14
15
|
const emit = defineEmits<{
|
|
15
16
|
(e: 'closed', value: boolean): void
|
|
@@ -35,6 +36,10 @@ const authApiURL = commonAuth.authApiURL
|
|
|
35
36
|
const accessToken = useCookie('accessToken')
|
|
36
37
|
const isAuthenticated = computed(() => !!accessToken.value)
|
|
37
38
|
|
|
39
|
+
// one-shot флаг: обмен по auth-сессии перезагружает страницу максимум один раз,
|
|
40
|
+
// иначе нечитаемая кука accessToken зацикливает reload
|
|
41
|
+
const AUTH_RELOAD_FLAG = 'auth-exchange-reloaded'
|
|
42
|
+
|
|
38
43
|
onMounted(async () => {
|
|
39
44
|
const response = await fetch(`${removeTrailingSlash(authApiURL)}/access/cookie`, {
|
|
40
45
|
method: 'GET',
|
|
@@ -46,17 +51,25 @@ onMounted(async () => {
|
|
|
46
51
|
const { data: cookiesData } = await response.json()
|
|
47
52
|
if (cookiesData?.access_token && !accessToken.value) {
|
|
48
53
|
const { access_token, expire_in } = cookiesData
|
|
49
|
-
const hostname = location.hostname.split('.').reverse()
|
|
50
54
|
useCookie('autoLogout').value = true
|
|
51
55
|
|
|
52
|
-
|
|
53
|
-
maxAge: expire_in,
|
|
54
|
-
domain: `.${hostname[1]}.${hostname[0]}`,
|
|
55
|
-
path: '/',
|
|
56
|
-
secure: true,
|
|
57
|
-
}).value = access_token
|
|
56
|
+
const persisted = await writeAccessTokenCookie(access_token, expire_in)
|
|
58
57
|
|
|
59
|
-
|
|
58
|
+
if (persisted && !sessionStorage.getItem(AUTH_RELOAD_FLAG)) {
|
|
59
|
+
sessionStorage.setItem(AUTH_RELOAD_FLAG, '1')
|
|
60
|
+
window.location.reload()
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
sessionStorage.removeItem(AUTH_RELOAD_FLAG)
|
|
64
|
+
console.warn(
|
|
65
|
+
persisted
|
|
66
|
+
? '[auth] accessToken cookie is erased between page loads (SSR resetTokens?), reload skipped to avoid a loop'
|
|
67
|
+
: '[auth] accessToken cookie was not persisted (domain/secure/maxAge rejected by browser), reload skipped',
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
sessionStorage.removeItem(AUTH_RELOAD_FLAG)
|
|
60
73
|
}
|
|
61
74
|
})
|
|
62
75
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { removeTrailingSlash } from '#adata-ui/components/utils/removeTrailingSlash'
|
|
3
2
|
import IdOtpInput from '#adata-ui/components/modals/id/IdOtpInput.vue'
|
|
3
|
+
import { removeTrailingSlash } from '#adata-ui/components/utils/removeTrailingSlash'
|
|
4
|
+
import { writeAccessTokenCookie } from '#adata-ui/composables/useAccessTokenCookie'
|
|
4
5
|
import { useUrls } from '#adata-ui/composables/useUrls'
|
|
5
6
|
|
|
6
7
|
const { $toast } = useNuxtApp()
|
|
@@ -54,15 +55,9 @@ async function onConfirm() {
|
|
|
54
55
|
const { data: cookiesData } = await response.json()
|
|
55
56
|
if (cookiesData?.access_token) {
|
|
56
57
|
const { access_token, expire_in } = cookiesData
|
|
57
|
-
const hostname = location.hostname.split('.').reverse()
|
|
58
58
|
useCookie('autoLogout').value = true
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
maxAge: expire_in,
|
|
62
|
-
domain: `.${hostname[1]}.${hostname[0]}`,
|
|
63
|
-
path: '/',
|
|
64
|
-
secure: true,
|
|
65
|
-
}).value = access_token
|
|
60
|
+
await writeAccessTokenCookie(access_token, expire_in)
|
|
66
61
|
}
|
|
67
62
|
$toast.success(t('login.successfully'))
|
|
68
63
|
twoFactorModal.value = false
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useUrls } from '#adata-ui/composables/useUrls'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Единая точка записи куки accessToken (логин, 2FA, OTP, обмен по auth-сессии).
|
|
5
|
+
* Домен должен совпадать с доменом удаления в resetTokens (adata-common-auth),
|
|
6
|
+
* иначе кука не находится на следующей загрузке и логин-флоу зацикливается на reload.
|
|
7
|
+
*/
|
|
8
|
+
export async function writeAccessTokenCookie(accessToken: string, expireIn?: number): Promise<boolean> {
|
|
9
|
+
const { mode } = useUrls()
|
|
10
|
+
const sharedDomain = `${mode}.kz`
|
|
11
|
+
const onSharedDomain = location.hostname === sharedDomain || location.hostname.endsWith(`.${sharedDomain}`)
|
|
12
|
+
const maxAge = Number(expireIn) > 0 ? Number(expireIn) : undefined
|
|
13
|
+
|
|
14
|
+
useCookie('accessToken', {
|
|
15
|
+
maxAge,
|
|
16
|
+
// на localhost/превью-хостах атрибут domain невалиден — пишем host-only куку
|
|
17
|
+
domain: onSharedDomain ? `.${sharedDomain}` : undefined,
|
|
18
|
+
path: '/',
|
|
19
|
+
secure: location.protocol === 'https:',
|
|
20
|
+
}).value = accessToken
|
|
21
|
+
|
|
22
|
+
// useCookie пишет document.cookie на следующем flush, не синхронно
|
|
23
|
+
await nextTick()
|
|
24
|
+
return document.cookie.split('; ').some(part => part.startsWith('accessToken='))
|
|
25
|
+
}
|