api/app/controllers/auth_controller.ts
2025-07-30 17:44:30 +02:00

171 lines
4.8 KiB
TypeScript

import type { HttpContext } from '@adonisjs/core/http'
import { registerValidator, requestLoginValidator, verifyCodeValidator } from '#validators/auth'
import mail from '@adonisjs/mail/services/main'
import { AuthService } from '#services/auth_service'
import { inject } from '@adonisjs/core'
import app from '@adonisjs/core/services/app'
import env from '#start/env'
import User from '#models/user'
@inject()
export default class AuthController {
constructor(private authService: AuthService) {}
// POST /auth/request
async requestLogin({ request, response, captcha }: HttpContext) {
// Validate captcha
if (app.inProduction) {
const validateResult = await (captcha.use('turnstile') as any).validate()
if (!validateResult.success) {
return response.badRequest({
success: false,
message: 'Veuillez valider le captcha',
error: validateResult.errorCodes,
})
}
}
const { email } = await request.validateUsing(requestLoginValidator)
// Generate token
const expiresIn = '15 minutes'
const payload = await this.authService.generateCode(email, expiresIn)
await mail
.send((message) => {
message
.from(env.get('MAIL_FROM')!)
.to(email)
.subject(payload.emailTitle)
.htmlView('mails/auth', payload)
.textView('mails/auth-fallback', payload)
})
// TODO: Handle email sending errors
.then(console.log)
.catch(console.error)
return {
success: true,
}
}
// POST /auth/verify
async verifyCode({ request, response, auth }: HttpContext) {
// Validate code
const { code } = await request.validateUsing(verifyCodeValidator)
const { success, email } = await this.authService.validateCode(code)
if (!success) {
return response.badRequest({
success: false,
message: 'Code de vérification invalide',
})
}
const user = await User.findBy('email', email)
if (!user) {
// If the user does not exist, return a token for registration
const expiresIn = '1 hour'
const { token, email: userEmail } = this.authService.generateToken(email, expiresIn)
return {
token,
email: userEmail,
success: true,
}
}
await auth.use('web').login(user, true) // true for remember me
return {
success: true,
}
}
// GET /auth/autocomplete
async listNames({ request }: HttpContext) {
const { className } = request.qs()
if (!className) {
return {
success: false,
message: 'Veuillez spécifier une classe',
}
}
return User.query()
.select('firstName', 'lastName')
.where('className', className)
.orderBy('lastName', 'asc')
.then((users) => {
return {
success: true,
data: users.map((user) => ({
value: `${user.firstName}::${user.lastName}`,
label: user.fullName,
})),
}
})
}
// POST /auth/register
async register({ request, response, auth }: HttpContext) {
const { name, className, token } = await request.validateUsing(registerValidator)
// Validate token
const { success, email } = this.authService.validateToken(token)
const [firstName, lastName] = name.split('::')
if (!success || !email || !firstName || !lastName) {
return response.badRequest({
success: false,
message: 'Votre lien de connexion est invalide ou a expiré.',
})
}
const user = await User.query()
.where('firstName', firstName)
.where('lastName', lastName)
.where('className', className)
.first()
if (!user) {
return response.badRequest({
success: false,
message: 'Utilisateur non trouvé. Veuillez vérifier vos informations.',
})
}
if (user.email && user.email.toLowerCase() !== email.toLowerCase()) {
return response.badRequest({
success: false,
message:
"L'email associé à votre compte ne correspond pas à celui utilisé pour la connexion.",
})
}
user.email = email.toLowerCase() // Update email if necessary
await user.save()
// Perform login
await auth.use('web').login(user, true) // true for remember me
return {
success: true,
}
}
// POST /auth/logout
async logout({ auth }: HttpContext) {
await auth.use('web').logout()
return {
success: true,
}
}
// TODO: Magic link login
// magicLink({ }: HttpContext) {
// // Validate signed url (adonis)
// // + login current device
// // + SSE to notify other devices (and login)
// }
// listen({ }: HttpContext) {
// // Listen for SSE events
// // Need an AUTH token to connect
// // AUTH token sent to client in requestLogin
// }
}