diff --git a/nuxt-kratos-selfservice/composables/useAuth.ts b/nuxt-kratos-selfservice/composables/useAuth.ts index d5b55c4..a1f8512 100644 --- a/nuxt-kratos-selfservice/composables/useAuth.ts +++ b/nuxt-kratos-selfservice/composables/useAuth.ts @@ -1,6 +1,11 @@ import { ref, readonly, onMounted } from "vue"; -import type { Session, LoginFlow, RegistrationFlow } from "@ory/kratos-client"; -import { FrontendApi, Configuration } from "@ory/kratos-client"; +import type { + Session, + LoginFlow, + RegistrationFlow, + UpdateLoginFlowBody, + UpdateRegistrationFlowBody, +} from "@ory/kratos-client"; interface AuthError { message: string; @@ -13,15 +18,7 @@ interface User { } export const useAuth = () => { - const config = useRuntimeConfig(); - const ory = new FrontendApi( - new Configuration({ - basePath: config.public.ORY_SDK_URL, - baseOptions: { - withCredentials: true, - }, - }) - ); + const { $kratos } = useNuxtApp(); const user = ref(null); const isAuthenticated = ref(false); @@ -64,7 +61,7 @@ export const useAuth = () => { isLoading.value = true; error.value = null; try { - const { data: session } = await ory.toSession(); + const { data: session } = await $kratos.toSession(); setUser(session); } catch (err) { error.value = handleError(err); @@ -79,7 +76,7 @@ export const useAuth = () => { isLoading.value = true; error.value = null; try { - const { data } = await ory.createBrowserLoginFlow(); + const { data } = await $kratos.createBrowserLoginFlow(); loginFlow.value = data; } catch (err) { error.value = handleError(err); @@ -88,16 +85,16 @@ export const useAuth = () => { } }; - const login = async (formData: Record) => { + const login = async (updateLoginFlowBody: UpdateLoginFlowBody) => { isLoading.value = true; error.value = null; try { if (!loginFlow.value) { throw new Error("Login flow not initialized"); } - const { data: successfulNativeLogin } = await ory.updateLoginFlow({ + const { data: successfulNativeLogin } = await $kratos.updateLoginFlow({ flow: loginFlow.value.id, - updateLoginFlowBody: formData, + updateLoginFlowBody, }); setUser(successfulNativeLogin.session); return true; @@ -113,9 +110,10 @@ export const useAuth = () => { isLoading.value = true; error.value = null; try { - await ory.createBrowserLogoutFlow(); + await $kratos.createBrowserLogoutFlow(); isAuthenticated.value = false; user.value = null; + return navigateTo("/login"); } catch (err) { error.value = handleError(err); } finally { @@ -127,7 +125,7 @@ export const useAuth = () => { isLoading.value = true; error.value = null; try { - const { data } = await ory.createBrowserRegistrationFlow(); + const { data } = await $kratos.createBrowserRegistrationFlow(); registrationFlow.value = data; } catch (err) { error.value = handleError(err); @@ -136,7 +134,9 @@ export const useAuth = () => { } }; - const register = async (formData: Record) => { + const register = async ( + updateRegistrationFlowBody: UpdateRegistrationFlowBody + ) => { isLoading.value = true; error.value = null; try { @@ -144,9 +144,9 @@ export const useAuth = () => { throw new Error("Registration flow not initialized"); } const { data: successfulNativeRegistration } = - await ory.updateRegistrationFlow({ + await $kratos.updateRegistrationFlow({ flow: registrationFlow.value.id, - updateRegistrationFlowBody: formData, + updateRegistrationFlowBody, }); if (!successfulNativeRegistration.session) { @@ -169,11 +169,6 @@ export const useAuth = () => { error.value = null; }; - // Check auth state on component mount - onMounted(() => { - checkAuth(); - }); - return { user: readonly(user), isAuthenticated: readonly(isAuthenticated), diff --git a/nuxt-kratos-selfservice/middleware/auth.global.ts b/nuxt-kratos-selfservice/middleware/auth.global.ts index 53a05ae..61c7d57 100644 --- a/nuxt-kratos-selfservice/middleware/auth.global.ts +++ b/nuxt-kratos-selfservice/middleware/auth.global.ts @@ -1,41 +1,50 @@ import { serverCheckAuth } from "~/server/utils/checkAuth"; import { useAuth } from "~/composables/useAuth"; +import type { RouteLocationNormalizedGeneric } from "vue-router"; export default defineNuxtRouteMiddleware(async (to) => { if (import.meta.server) { + if (!to.path.startsWith("/api")) { + return; + } + const event = useRequestEvent(); if (!event) { - console.error('Request event is undefined'); return; } + const session = await serverCheckAuth(event); - try { - const session = await serverCheckAuth(event); - if (!session && to.meta.requiresAuth !== false && to.meta.guestOnly !== true) { - return navigateTo("/login"); - } - if (session && to.meta.guestOnly) { - return navigateTo("/"); - } - } catch (error) { - console.error('Server-side auth check failed:', error); - return navigateTo("/login"); - } - } else { + return checkRoute(session, to); + } + + if (import.meta.client) { const { checkAuth, isAuthenticated, isLoading } = useAuth(); - + await checkAuth(); if (isLoading.value) { return; } - if (to.meta.requiresAuth !== false && to.meta.guestOnly !== true && !isAuthenticated.value) { - return navigateTo("/login"); - } - - if (to.meta.guestOnly && isAuthenticated.value) { - return navigateTo("/"); - } + return checkRoute(isAuthenticated.value, to); } }); + +const checkRoute = ( + session: boolean, + route: RouteLocationNormalizedGeneric +) => { + if ( + !session && + route.meta.requiresAuth !== false && + route.meta.guestOnly !== true + ) { + return navigateTo("/login"); + } + + if (session && route.meta.guestOnly) { + return navigateTo("/"); + } + + return; +}; diff --git a/nuxt-kratos-selfservice/plugins/kratos.ts b/nuxt-kratos-selfservice/plugins/kratos.ts index 373a25b..2a0c88d 100644 --- a/nuxt-kratos-selfservice/plugins/kratos.ts +++ b/nuxt-kratos-selfservice/plugins/kratos.ts @@ -1,14 +1,6 @@ import { defineNuxtPlugin } from "#app"; import { Configuration, FrontendApi } from "@ory/kratos-client"; -const kratos = { - public: {}, - api: {}, -} as { - public: FrontendApi; - api: FrontendApi; -}; - const createClient = (url: string) => { return new FrontendApi( new Configuration({ @@ -22,14 +14,7 @@ const createClient = (url: string) => { export default defineNuxtPlugin((nuxtApp) => { const config = useRuntimeConfig(); - - if (!kratos.public) { - kratos.public = createClient(config.public.ORY_SDK_URL); - } - - if (!kratos.api) { - kratos.api = createClient(config.ORY_SDK_URL); - } + const kratos = createClient(config.public.ORY_SDK_URL); return { provide: { diff --git a/nuxt-kratos-selfservice/server/utils/checkAuth.ts b/nuxt-kratos-selfservice/server/utils/checkAuth.ts index 2937458..9e0b2eb 100644 --- a/nuxt-kratos-selfservice/server/utils/checkAuth.ts +++ b/nuxt-kratos-selfservice/server/utils/checkAuth.ts @@ -1,33 +1,33 @@ import { getCookie } from "h3"; import { useRuntimeConfig } from "#imports"; import type { H3Event } from "h3"; +import { Configuration, FrontendApi } from "@ory/kratos-client"; + +const createKratosClient = (basePath: string) => + new FrontendApi( + new Configuration({ + basePath, + }) + ); export async function serverCheckAuth(event: H3Event) { const config = useRuntimeConfig(); const cookie = getCookie(event, "ory_kratos_session"); + const client = createKratosClient(config.ORY_SDK_URL); - if (!cookie) { - console.warn("No session cookie found"); - return null; - } - - try { - const response = await fetch(`${config.ORY_SDK_URL}/sessions/whoami`, { - method: "GET", - headers: { - Cookie: `ory_kratos_session=${cookie}`, - Accept: "application/json", - }, + return client + .toSession({ + xSessionToken: cookie, + }) + .then(({ data, status }) => { + if (status !== 200) { + console.error(`HTTP error! status: ${status}`); + return false; + } + return !!data.active && !!data.id && !!data.identity; + }) + .catch((error) => { + console.error("Error checking authentication:", error); + return false; }); - - if (!response.ok) { - console.error(`HTTP error! status: ${response.status}`); - return null; - } - - return await response.json(); - } catch (error) { - console.error("Error checking authentication:", error); - return null; - } }