diff --git a/src/assets/css/auth.css b/src/assets/css/auth.css index e2598a3..97cd61d 100644 --- a/src/assets/css/auth.css +++ b/src/assets/css/auth.css @@ -120,6 +120,12 @@ form.account.register { column-gap: 24px; margin-bottom: 48px; } +form.account.loading { + display: flex; + align-items: center; + text-align: center; + justify-content: center; +} form.account.register .h-captcha { grid-column: 1 / span 2; diff --git a/src/composables/useLogin.ts b/src/composables/useLogin.ts new file mode 100644 index 0000000..62fc6cd --- /dev/null +++ b/src/composables/useLogin.ts @@ -0,0 +1,61 @@ +import { FetchError } from 'ofetch'; +import type { ApiAuthRefreshRequest } from '~~/shared/api-types'; + +export function useLogin() { + const authStore = useAuthStore(); + const meStore = useMeStore(); + + async function handleRefresh(): Promise<{ hasNewTokens: boolean }> { + if (meStore.user) { + return { hasNewTokens: false }; // You're good, nothing to do + } + + const tokens = authStore.getTokens(); + if (!tokens || !tokens.refreshToken) { + return { hasNewTokens: false }; // No tokens to use + } + + try { + const result = await $fetch('/api/auth/refresh', { + method: 'POST', + body: { + token: tokens?.refreshToken ?? '' + } satisfies ApiAuthRefreshRequest + }); + authStore.set({ + accessToken: result.accessToken, + refreshToken: result.refreshToken + }); + return { hasNewTokens: true }; + } catch (err) { + if (err instanceof FetchError) { + if (err.statusCode === 401) { + // Refresh token expired, log out + authStore.logout(); + } + } + return { hasNewTokens: false }; + } + } + + function isLoggedIn() { + if (meStore.user) { + return true; + } + return false; + } + + function canRefresh() { + const tokens = authStore.getTokens(); + if (!tokens || !tokens.refreshToken) { + return false; // No refresh token to use + } + return true; + } + + return { + handleRefresh, + canRefresh, + isLoggedIn + }; +} diff --git a/src/pages/account/login.vue b/src/pages/account/login.vue index a495277..4347c88 100644 --- a/src/pages/account/login.vue +++ b/src/pages/account/login.vue @@ -14,6 +14,14 @@ const registerURI = computed(() => { return `/account/register`; }); +const login = useLogin(); +const { status } = useAsyncData(async () => { + const result = await login.handleRefresh(); + if (result.hasNewTokens || login.isLoggedIn()) { + await authUtils.safelyRedirectAfterLogin(redirect.value); + } +}, { server: false }); + const loginForm = reactive({ username: '', password: '' }); const { execute, isLoading } = useAsync({ @@ -42,7 +50,16 @@ const { execute, isLoading } = useAsync({