gestion du login et de ses erreurs
This commit is contained in:
21
package-lock.json
generated
21
package-lock.json
generated
@@ -9,6 +9,7 @@
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@fastify/cookie": "^11.0.2",
|
||||
"@prisma/adapter-pg": "^7.6.0",
|
||||
"@prisma/client": "^7.6.0",
|
||||
"argon2": "^0.44.0",
|
||||
@@ -548,6 +549,26 @@
|
||||
"fast-uri": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@fastify/cookie": {
|
||||
"version": "11.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@fastify/cookie/-/cookie-11.0.2.tgz",
|
||||
"integrity": "sha512-GWdwdGlgJxyvNv+QcKiGNevSspMQXncjMZ1J8IvuDQk0jvkzgWWZFNC2En3s+nHndZBGV8IbLwOI/sxCZw/mzA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fastify"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fastify"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cookie": "^1.0.0",
|
||||
"fastify-plugin": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@fastify/error": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@fastify/error/-/error-4.2.0.tgz",
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"author": "Raffi",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@fastify/cookie": "^11.0.2",
|
||||
"@prisma/adapter-pg": "^7.6.0",
|
||||
"@prisma/client": "^7.6.0",
|
||||
"argon2": "^0.44.0",
|
||||
|
||||
@@ -13,5 +13,6 @@ export class AppError extends Error {
|
||||
export const Errors = {
|
||||
EMAIL_TAKEN: new AppError('EMAIL_TAKEN', 409, 'Cette adresse email est déjà utilisée.'),
|
||||
PASSWORD_TOO_WEAK: new AppError('PASSWORD_TOO_WEAK', 400, 'Le mot de passe doit contenir au moins 8 caractères.'),
|
||||
INVALID_CREDENTIALS: new AppError('INVALID_CREDENTIALS', 401, 'Email ou mot de passe incorrect.'),
|
||||
VALIDATION_ERROR: (message: string) => new AppError('VALIDATION_ERROR', 400, message),
|
||||
}
|
||||
@@ -2,6 +2,9 @@ import { FastifyInstance } from 'fastify'
|
||||
import { RegisterSchema } from '../schemas/auth.schema.js'
|
||||
import { registerUser } from '../services/auth.service.js'
|
||||
|
||||
import { LoginSchema } from '../schemas/auth.schema.js'
|
||||
import { loginUser } from '../services/auth.service.js'
|
||||
|
||||
export default async function authRoutes(fastify: FastifyInstance) {
|
||||
fastify.post('/auth/register', async (request, reply) => {
|
||||
const body = RegisterSchema.parse(request.body) // Zod throw → handler global
|
||||
@@ -20,4 +23,18 @@ export default async function authRoutes(fastify: FastifyInstance) {
|
||||
|
||||
return reply.status(201).send({ user })
|
||||
})
|
||||
|
||||
fastify.post('/auth/login', async (request, reply) => {
|
||||
const body = LoginSchema.parse(request.body)
|
||||
|
||||
const { user, authToken } = await loginUser(fastify.prisma, body)
|
||||
|
||||
reply.setCookie('authToken', authToken, {
|
||||
httpOnly: true,
|
||||
sameSite: 'strict',
|
||||
maxAge: 60 * 60 * 24 * 7,
|
||||
})
|
||||
|
||||
return reply.status(200).send({ user })
|
||||
})
|
||||
}
|
||||
@@ -11,4 +11,11 @@ export const RegisterSchema = z.object({
|
||||
.regex(/\d/, { error: 'Le mot de passe doit contenir au moins un chiffre.' }),
|
||||
})
|
||||
|
||||
export type RegisterInput = z.infer<typeof RegisterSchema>
|
||||
export type RegisterInput = z.infer<typeof RegisterSchema>
|
||||
|
||||
export const LoginSchema = z.object({
|
||||
email: z.email({ error: 'Adresse email invalide.' }),
|
||||
password: z.string().min(1, { error: 'Le mot de passe est requis.' }),
|
||||
})
|
||||
|
||||
export type LoginInput = z.infer<typeof LoginSchema>
|
||||
@@ -2,7 +2,7 @@ import argon2 from 'argon2'
|
||||
import crypto from 'crypto'
|
||||
import { PrismaClient } from '../generated/prisma/client.js'
|
||||
import { Transporter } from 'nodemailer'
|
||||
import { RegisterInput } from '../schemas/auth.schema.js'
|
||||
import { RegisterInput, LoginInput } from '../schemas/auth.schema.js'
|
||||
import { generateToken, generateAuthTokenExpiry, generateActionTokenExpiry } from './token.service.js'
|
||||
import { sendConfirmationMail } from './mail.service.js'
|
||||
import { Errors } from '../errors/AppError.js'
|
||||
@@ -68,4 +68,46 @@ export async function registerUser(
|
||||
})
|
||||
|
||||
return { user, authToken }
|
||||
}
|
||||
|
||||
export async function loginUser(
|
||||
prisma: PrismaClient,
|
||||
input: LoginInput
|
||||
) {
|
||||
// 1. Vérif user existe
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { email: input.email },
|
||||
})
|
||||
|
||||
if (!user) {
|
||||
throw Errors.INVALID_CREDENTIALS
|
||||
}
|
||||
|
||||
// 2. Vérif password
|
||||
const valid = await argon2.verify(user.passwordHash, input.password)
|
||||
if (!valid) {
|
||||
throw Errors.INVALID_CREDENTIALS
|
||||
}
|
||||
|
||||
// 3. Création AuthToken
|
||||
const authToken = generateToken()
|
||||
await prisma.authToken.create({
|
||||
data: {
|
||||
id: crypto.randomUUID(),
|
||||
userId: user.id,
|
||||
token: authToken,
|
||||
expiresAt: generateAuthTokenExpiry(),
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
user: {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
displayName: user.displayName,
|
||||
isConfirmed: user.isConfirmed,
|
||||
createdAt: user.createdAt,
|
||||
},
|
||||
authToken,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user