Skip to content
Snippets Groups Projects
Commit 9e669cb8 authored by Sofiane Lasri's avatar Sofiane Lasri
Browse files

feat(auth): enhance session management and improve user flow

- Add type annotations for H3Event and EventHandlerRequest
- Refactor session generation to include user info in cookies
- Improve password validation for non-development environments
- Simplify session setting in login and register handlers
- Adjust routing logic after login and registration
- Remove redundant router pushes in auth store functions
- Update auth middleware to use session token for validation
- Display error messages on login and registration failures
parent 8d9afbe8
Branches
No related tags found
No related merge requests found
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
<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>
......
<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>
......
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,
......
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'});
......@@ -10,7 +11,7 @@ export default defineEventHandler(async (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,
......
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,19 +19,48 @@ 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,
......
......@@ -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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment