@@ -134,7 +134,7 @@ const otpApi: FastifyPluginCallback = (fastify, _, done) => {
134134 fingerprint ?: string ;
135135 } ;
136136 } > ( '/api/auth/request-otp' , async function ( req , reply ) {
137- const { phoneNumber, email, geolocation , resolution , source , fingerprint } = req . body ;
137+ const { phoneNumber, email } = req . body ;
138138
139139 // Validate that exactly one of phoneNumber or email is provided
140140 if ( ( phoneNumber == null && email == null ) || ( phoneNumber != null && email != null ) ) {
@@ -192,6 +192,93 @@ const otpApi: FastifyPluginCallback = (fastify, _, done) => {
192192 } ) ;
193193 }
194194
195+ // Validate that the user has the requested email/phone registered
196+ if ( email != null ) {
197+ const userEmails = user . emails ?? [ ] ;
198+ const hasEmail = userEmails . some ( e => e . address ?. toLowerCase ( ) === email . toLowerCase ( ) ) ;
199+ if ( ! hasEmail ) {
200+ const userAgent = req . headers [ 'user-agent' ] ;
201+ const ua = new UAParser ( userAgent ?? 'API Call' ) . getResult ( ) ;
202+ const ip = extractIp ( req ) ;
203+
204+ const accessLog = {
205+ _id : randomId ( ) ,
206+ _createdAt : new Date ( ) ,
207+ _updatedAt : new Date ( ) ,
208+ ip,
209+ login : identifier ,
210+ browser : ua . browser . name ,
211+ browserVersion : ua . browser . version ,
212+ os : ua . os . name ,
213+ platform : ua . device . type ,
214+ reason : `Email [${ email } ] not registered in user account` ,
215+ __from : 'request-otp' ,
216+ _user : [
217+ {
218+ _id : user . _id ?. toString ( ) || `${ user . _id } ` ,
219+ name : user . name ,
220+ group : user . group ,
221+ } ,
222+ ] ,
223+ } ;
224+
225+ await MetaObject . Collections . AccessFailedLog . insertOne ( accessLog as DataDocument ) ;
226+
227+ return reply . status ( StatusCodes . BAD_REQUEST ) . send ( {
228+ success : false ,
229+ errors : [ { message : 'Este email não está cadastrado no sistema' } ] ,
230+ } ) ;
231+ }
232+ }
233+
234+ if ( phoneNumber != null ) {
235+ const userDoc = ( await MetaObject . Collections . User . findOne ( { _id : user . _id } , { projection : { phone : 1 } } ) ) as {
236+ phone ?: Array < { phoneNumber ?: string ; countryCode ?: number } > ;
237+ } | null ;
238+ const userPhones = userDoc ?. phone ?? [ ] ;
239+ const normalizedPhone = phoneNumber . replace ( / [ + \s ] / g, '' ) ;
240+ const hasPhone = userPhones . some ( ( p : { phoneNumber ?: string ; countryCode ?: number } ) => {
241+ if ( p . phoneNumber == null ) {
242+ return false ;
243+ }
244+ const phoneStr = `${ p . countryCode ?? '' } ${ p . phoneNumber } ` . replace ( / [ + \s ] / g, '' ) ;
245+ return phoneStr === normalizedPhone || p . phoneNumber . replace ( / [ + \s ] / g, '' ) === normalizedPhone ;
246+ } ) ;
247+ if ( ! hasPhone ) {
248+ const userAgent = req . headers [ 'user-agent' ] ;
249+ const ua = new UAParser ( userAgent ?? 'API Call' ) . getResult ( ) ;
250+ const ip = extractIp ( req ) ;
251+
252+ const accessLog = {
253+ _id : randomId ( ) ,
254+ _createdAt : new Date ( ) ,
255+ _updatedAt : new Date ( ) ,
256+ ip,
257+ login : identifier ,
258+ browser : ua . browser . name ,
259+ browserVersion : ua . browser . version ,
260+ os : ua . os . name ,
261+ platform : ua . device . type ,
262+ reason : `Phone number [${ phoneNumber } ] not registered in user account` ,
263+ __from : 'request-otp' ,
264+ _user : [
265+ {
266+ _id : user . _id ?. toString ( ) || `${ user . _id } ` ,
267+ name : user . name ,
268+ group : user . group ,
269+ } ,
270+ ] ,
271+ } ;
272+
273+ await MetaObject . Collections . AccessFailedLog . insertOne ( accessLog as DataDocument ) ;
274+
275+ return reply . status ( StatusCodes . BAD_REQUEST ) . send ( {
276+ success : false ,
277+ errors : [ { message : 'Este telefone não está cadastrado no sistema' } ] ,
278+ } ) ;
279+ }
280+ }
281+
195282 // Create OTP request (rate limiting is handled inside createOtpRequest via database transaction)
196283 const getOtpResult = async ( ) : Promise < { otpRequest : OtpRequest ; otpCode : string } > => {
197284 try {
@@ -216,9 +303,28 @@ const otpApi: FastifyPluginCallback = (fastify, _, done) => {
216303
217304 if ( ! deliveryResult . success ) {
218305 logger . error ( `Failed to send OTP: ${ deliveryResult . error } ` ) ;
306+
307+ // Map specific errors to user-friendly messages
308+ const errorMessage = deliveryResult . error ?? 'Erro desconhecido ao enviar OTP' ;
309+ let userMessage = 'Falha no envio do código de verificação. Tente novamente mais tarde.' ;
310+
311+ if ( errorMessage . includes ( 'User does not have an email address' ) || errorMessage . includes ( 'does not have an email' ) ) {
312+ userMessage = 'O email informado não está cadastrado no sistema' ;
313+ } else if ( errorMessage . includes ( 'User not found' ) ) {
314+ userMessage = 'Email ou telefone não encontrado no sistema' ;
315+ } else if ( errorMessage . includes ( 'WhatsApp configuration not available' ) || errorMessage . includes ( 'WhatsApp' ) ) {
316+ userMessage = 'Serviço de WhatsApp temporariamente indisponível. Tente novamente mais tarde.' ;
317+ } else if ( errorMessage . includes ( 'RabbitMQ' ) || errorMessage . includes ( 'queue' ) ) {
318+ userMessage = 'Serviço de mensagens temporariamente indisponível. Tente novamente mais tarde.' ;
319+ } else if ( errorMessage . includes ( 'Failed to render email template' ) ) {
320+ userMessage = 'Erro ao processar template de email. Entre em contato com o suporte.' ;
321+ } else if ( errorMessage . includes ( 'Failed to create message' ) ) {
322+ userMessage = 'Erro ao criar mensagem. Tente novamente mais tarde.' ;
323+ }
324+
219325 return reply . status ( StatusCodes . INTERNAL_SERVER_ERROR ) . send ( {
220326 success : false ,
221- errors : [ { message : 'Failed to send OTP. Please try again later.' } ] ,
327+ errors : [ { message : userMessage } ] ,
222328 } ) ;
223329 }
224330
@@ -522,17 +628,14 @@ const otpApi: FastifyPluginCallback = (fastify, _, done) => {
522628 const hashStampedToken = generateStampedLoginToken ( ) ;
523629
524630 // Update user
525- await MetaObject . Collections . User . updateOne (
526- { _id : user . _id } ,
527- {
528- $set : {
529- lastLogin : new Date ( ) ,
530- } ,
531- $push : {
532- 'services.resume.loginTokens' : hashStampedToken ,
533- } ,
534- } as any ,
535- ) ;
631+ await MetaObject . Collections . User . updateOne ( { _id : user . _id } , {
632+ $set : {
633+ lastLogin : new Date ( ) ,
634+ } ,
635+ $push : {
636+ 'services.resume.loginTokens' : hashStampedToken ,
637+ } ,
638+ } as any ) ;
536639
537640 // Create AccessLog with all available data (same format as traditional login)
538641 const userAgent = req . headers [ 'user-agent' ] ;
0 commit comments