171 lines
		
	
	
	
		
			4.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			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
 | |
|   // }
 | |
| }
 | 
