diff --git a/middleware/auth.ts b/middleware/auth.ts index caeee180f10ba045b8a3571a77f65bd2bc488839..5048b24c316618f701b82aebc25c6fd56cb1698a 100644 --- a/middleware/auth.ts +++ b/middleware/auth.ts @@ -1,6 +1,6 @@ export default defineNuxtRouteMiddleware((to) => { - const isAuthenticated = false; // TODO: check if user is authenticated - if (!isAuthenticated && to.path.startsWith('/admin')) { + const hasToken = useCookie('session_token'); + if (!hasToken && to.path.startsWith('/admin')) { return navigateTo('/login'); } }); \ No newline at end of file diff --git a/pages/login.vue b/pages/login.vue index 48c84f5a2c6ee18885004b922e12d7237c92e37f..03db95bded31c6b3603109aeffa49e228a0f2b7f 100644 --- a/pages/login.vue +++ b/pages/login.vue @@ -1,5 +1,6 @@ <script setup lang="ts"> import type {FormError} from "#ui/types"; +import {useRouter} from "#vue-router"; definePageMeta({ layout: 'auth', @@ -48,6 +49,7 @@ const validate = (values: any) => { const authStore = useAuthStore(); const loginErrorMessage = ref<string | null>(null); const isLoggingIn = ref(false); +const router = useRouter(); async function onSubmit(data: any) { isLoggingIn.value = true; @@ -58,6 +60,7 @@ async function onSubmit(data: any) { loginErrorMessage.value = 'Identifiants incorrects'; } finally { isLoggingIn.value = false; + await router.push('/admin'); } } </script> diff --git a/pages/register.vue b/pages/register.vue index 6cb59744802660547345abfdc7954d2531d6308c..a3091d1ed2ae817631f8e333b3b71309a744282b 100644 --- a/pages/register.vue +++ b/pages/register.vue @@ -1,5 +1,6 @@ <script setup lang="ts"> import type {FormError} from "#ui/types"; +import {useRouter} from "#vue-router"; definePageMeta({ layout: 'auth', middleware: 'verify-user-doesnt-exists' @@ -52,7 +53,7 @@ const validate = (values: any) => { if (values.password.length < 6 || values.password.length > 32) { errors.push({path: 'password', message: 'Votre mot de passe doit contenir entre 6 et 32 caractères'}); } - if (!/[A-Z]/.test(values.password) || !/[a-z]/.test(values.password) || !/[0-9]/.test(values.password) || !/[^A-Za-z0-9]/.test(values.password)) { + if ((!/[A-Z]/.test(values.password) || !/[a-z]/.test(values.password) || !/[0-9]/.test(values.password) || !/[^A-Za-z0-9]/.test(values.password)) && process.env.NODE_ENV !== 'development') { errors.push({path: 'password', message: 'Votre mot de passe doit contenir au moins une majuscule, une minuscule, un chiffre et un caractère spécial'}); } } @@ -66,10 +67,22 @@ const validate = (values: any) => { return errors; }; +const authStore = useAuthStore(); const isRegisteringIn = ref(false); +const registerErrorMessage = ref<string | null>(null); +const router = useRouter(); async function onSubmit(data: any) { - console.log('Registering in with data', data); + isRegisteringIn.value = true; + try { + await authStore.register(data.email, data.password, data.name); + } catch (error) { + console.error('Register failed', error); + registerErrorMessage.value = 'Une erreur est survenue lors de la création de votre compte'; + } finally { + isRegisteringIn.value = false; + await router.push('/admin'); + } } </script> @@ -86,6 +99,10 @@ async function onSubmit(data: any) { :ui="uiOptions" @submit="onSubmit" > + <template #validation v-if="registerErrorMessage !== null"> + <UAlert color="red" icon="i-heroicons-information-circle-20-solid" + :title="registerErrorMessage" /> + </template> </UAuthForm> </UCard> </template> diff --git a/server/api/auth/login.ts b/server/api/auth/login.ts index 1e83da56a50780f09381c4c08d450d11e3925f98..f2d5a99402a834a3597019f92c3eb2dd6d2b4905 100644 --- a/server/api/auth/login.ts +++ b/server/api/auth/login.ts @@ -1,8 +1,9 @@ import {compare} from 'bcrypt'; import {generateSession} from "~/server/utils/generateSession"; import prisma from "~/lib/prisma"; +import {EventHandlerRequest, H3Event} from "h3"; -export default defineEventHandler(async (event) => { +export default defineEventHandler(async (event: H3Event<EventHandlerRequest>) => { const userExists = await prisma.user.count(); if (!userExists) { throw createError({statusCode: 403, statusMessage: 'No users exist'}); @@ -18,14 +19,7 @@ export default defineEventHandler(async (event) => { throw createError({statusCode: 401, statusMessage: 'Invalid credentials'}); } - const generatedSession = await generateSession(user.id); - - setCookie(event, 'session', generatedSession.token, { - expires: generatedSession.expiresAt, - httpOnly: true, - secure: true, - sameSite: 'strict', - }); + const generatedSession = await generateSession(user, event); return { id: user.id, diff --git a/server/api/auth/register.ts b/server/api/auth/register.ts index 8c219325a2f909d0e75e3b160d3c7ef9f8d59f9e..42f8369802b196f726d12232ef5cb3e70534d28f 100644 --- a/server/api/auth/register.ts +++ b/server/api/auth/register.ts @@ -1,16 +1,17 @@ -import { hash } from 'bcrypt'; +import {hash} from 'bcrypt'; import {generateSession} from "~/server/utils/generateSession"; import prisma from "~/lib/prisma"; +import {EventHandlerRequest, H3Event} from "h3"; -export default defineEventHandler(async (event) => { +export default defineEventHandler(async (event: H3Event<EventHandlerRequest>) => { const userExists = await prisma.user.count(); if (userExists) { - throw createError({ statusCode: 403, statusMessage: 'User already exists' }); + throw createError({statusCode: 403, statusMessage: 'User already exists'}); } - const { email, name, password } = await readBody(event); + const {email, name, password} = await readBody(event); - if (!/[A-Z]/.test(password) || !/[a-z]/.test(password) || !/[0-9]/.test(password) || !/[^A-Za-z0-9]/.test(password)) { + if ((!/[A-Z]/.test(password) || !/[a-z]/.test(password) || !/[0-9]/.test(password) || !/[^A-Za-z0-9]/.test(password)) && process.env.NODE_ENV !== 'development') { return new Response('Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character', { status: 422, }); @@ -26,14 +27,7 @@ export default defineEventHandler(async (event) => { }, }); - const generatedSession = await generateSession(user.id); - - setCookie(event, 'session', generatedSession.token, { - expires: generatedSession.expiresAt, - httpOnly: true, - secure: true, - sameSite: 'strict', - }); + await generateSession(user, event); return { id: user.id, diff --git a/server/utils/generateSession.ts b/server/utils/generateSession.ts index 7c143b08f73f97adf34341be29f6cd70dfcfeedd..6c58b74be1c79a61533aca2e1b91af9578f0751f 100644 --- a/server/utils/generateSession.ts +++ b/server/utils/generateSession.ts @@ -1,5 +1,7 @@ import * as crypto from 'crypto'; import prisma from "~/lib/prisma"; +import {EventHandlerRequest, H3Event} from "h3"; +import {User} from "@prisma/client"; async function generateSessionToken(): Promise<string> { let generatedToken = crypto.randomBytes(32).toString('hex'); @@ -17,23 +19,52 @@ async function generateSessionToken(): Promise<string> { return generatedToken; } -async function generateSession(userId: number): Promise<{ expiresAt: Date; token: string }> { +/** + * Generates a session for a user and sets a cookie with the session token + * @param user The ID of the user to generate a session for + * @param event The H3 event to set the cookie on + */ +async function generateSession(user: User, event: H3Event<EventHandlerRequest>): Promise<{ + expiresAt: Date; + token: string +}> { const token = await generateSessionToken(); const expirationDate = new Date(); expirationDate.setDate(expirationDate.getDate() + 7); - prisma.session.create({ + const session = await prisma.session.create({ data: { token, - userId, + userId: user.id, expiresAt: expirationDate, } }); + setCookie(event, 'session_token', session.token, { + expires: session.expiresAt, + httpOnly: true, + secure: true, + sameSite: 'strict', + }); + + setCookie(event, 'userId', session.userId.toString(), { + expires: session.expiresAt, + httpOnly: false, + secure: true, + sameSite: 'strict', + }); + + setCookie(event, 'name', user.name, { + expires: session.expiresAt, + httpOnly: false, + secure: true, + sameSite: 'strict', + }); + return { token, expiresAt: expirationDate, }; } -export { generateSession }; \ No newline at end of file +export {generateSession}; \ No newline at end of file diff --git a/stores/auth.ts b/stores/auth.ts index f0c899fa00d53b0df9cae5a735e05423d06f5f64..4cd60350ac7c6acc975d98938cba5d3be27e9599 100644 --- a/stores/auth.ts +++ b/stores/auth.ts @@ -24,8 +24,6 @@ export const useAuthStore = defineStore('auth', { } this.user = await response.json(); - const router = useRouter(); - await router.push('/admin'); } catch (error) { console.error(error); } @@ -48,17 +46,13 @@ export const useAuthStore = defineStore('auth', { } this.user = await response.json(); - const router = useRouter(); - await router.push('/admin'); } catch (error) { console.error(error); } }, - async logout() { + logout() { this.user = null; - const router = useRouter(); - await router.push('/'); } } }) \ No newline at end of file