Skip to content

Commit

Permalink
Improved code examples for nuxt + vue3 + kratos.
Browse files Browse the repository at this point in the history
  • Loading branch information
khusseini committed Oct 14, 2024
1 parent 311e3a6 commit f92759d
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 87 deletions.
47 changes: 21 additions & 26 deletions nuxt-kratos-selfservice/composables/useAuth.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<User | null>(null);
const isAuthenticated = ref(false);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -88,16 +85,16 @@ export const useAuth = () => {
}
};

const login = async (formData: Record<string, string>) => {
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;
Expand All @@ -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 {
Expand All @@ -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);
Expand All @@ -136,17 +134,19 @@ export const useAuth = () => {
}
};

const register = async (formData: Record<string, string>) => {
const register = async (
updateRegistrationFlowBody: UpdateRegistrationFlowBody
) => {
isLoading.value = true;
error.value = null;
try {
if (!registrationFlow.value) {
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) {
Expand All @@ -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),
Expand Down
53 changes: 31 additions & 22 deletions nuxt-kratos-selfservice/middleware/auth.global.ts
Original file line number Diff line number Diff line change
@@ -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;
};
17 changes: 1 addition & 16 deletions nuxt-kratos-selfservice/plugins/kratos.ts
Original file line number Diff line number Diff line change
@@ -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({
Expand All @@ -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: {
Expand Down
46 changes: 23 additions & 23 deletions nuxt-kratos-selfservice/server/utils/checkAuth.ts
Original file line number Diff line number Diff line change
@@ -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;
}
}

0 comments on commit f92759d

Please sign in to comment.