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) // Send email 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', }) } // Find user by id 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, } } // Perform login await auth.use('web').login(user, true) // true for remember me return { success: true, user } } // POST /auth/register async register({ request, response, auth }: HttpContext) { const { firstName, lastName, className, token } = await request.validateUsing(registerValidator) // Validate token const { success, email } = this.authService.validateToken(token) if (!success || !email) { return response.badRequest({ success: false, message: 'Votre lien de connexion est invalide ou a expiré.', }) } // Check if user already exists const existingUser = await User.findBy('email', email) if (existingUser) { // If user already exists, perform login await auth.use('web').login(existingUser, true) // true for remember me return { success: true, user: existingUser, } } // TODO: Check if className is allowed (else redirect for account giving) // TODO: Rewrite user creation (NEVER CREATE USER - use string similarity) // Create new user const user = await User.create({ firstName, lastName, className, email }) // Perform login await auth.use('web').login(user, true) // true for remember me return { success: true, user } } // 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 // } }