import psl from 'psl' ; import { SKIPPED_LIST_ITEM_TYPES , VERIFICATION_CODE_PROTECTED_TYPES , VERIFICATION_CODE_STATUS , VERIFICATION_DOMAIN_WHITELIST , } from 'common/enums' ; import { AuthenticationError , EmailExistsError , EmailNotFoundError , ForbiddenError , UserInputError , } from 'common/errors' ; import logger from 'common/logger' ; import { gcp } from 'connectors' ; import { GQLVerificationCodeType , MutationToSendVerificationCodeResolver , } from 'definitions' ; const resolver = async ( _ , { input : { email : rawEmail , type , token , redirectUrl } } , { viewer , dataSources : { atomService , userService , notificationService , systemService , } , } ) => { const email = rawEmail . toLowerCase ( ) ; if ( ! viewer . id && VERIFICATION_CODE_PROTECTED_TYPES . includes ( type ) ) { throw new AuthenticationError ( `visitor cannot send verification code of ${ type }` ) ; } let user ; if ( type === GQLVerificationCodeType . register ) { user = await userService . findByEmail ( email ) ; if ( user ) { throw new EmailExistsError ( 'email has been registered' ) ; } const isHuman = await gcp . recaptcha ( { token , ip : viewer . ip } ) ; if ( ! isHuman ) { throw new ForbiddenError ( 'registration via scripting is not allowed' ) ; } } if ( type === GQLVerificationCodeType . payment_password_reset || type === GQLVerificationCodeType . password_reset || type === GQLVerificationCodeType . email_reset ) { user = await userService . findByEmail ( email ) ; if ( ! user ) { throw new EmailNotFoundError ( 'cannot find email' ) ; } } if ( redirectUrl ) { const url = new URL ( redirectUrl ) ; const tld = url ?. hostname ? psl . get ( url . hostname ) : null ; if ( tld && ! VERIFICATION_DOMAIN_WHITELIST . includes ( tld ) ) { throw new UserInputError ( '"redirectUrl" is invalid.' ) ; } } const { agentHash } = viewer ; const { AGENT_HASH : TYPE_HASH , EMAIL : TYPE_EMAIL , DOMAIN : TYPE_DOMAIN , } = SKIPPED_LIST_ITEM_TYPES ; const feature = await systemService . getFeatureFlag ( 'fingerprint' ) ; const isFingerprintEnabled = feature && ( await systemService . isFeatureEnabled ( feature . flag , viewer ) ) ; const banEmail = await systemService . findSkippedItem ( TYPE_EMAIL , email ) ; if ( banEmail && banEmail . archived === false ) { if ( agentHash ) { await systemService . createSkippedItem ( { type : TYPE_HASH , uuid : banEmail . uuid , value : agentHash , } ) ; } logger . info ( new Error ( `email ${ email } is in blocklist` ) ) ; if ( isFingerprintEnabled ) { return true ; } } const domain = email . split ( '@' ) [ 1 ] ; const banDomain = await systemService . findSkippedItem ( TYPE_DOMAIN , domain ) ; if ( banDomain && banDomain . archived === false ) { logger . info ( new Error ( `domain ${ domain } is in blocklist` ) ) ; return true ; } if ( agentHash ) { const banAgentHash = await systemService . findSkippedItem ( TYPE_HASH , agentHash ) ; if ( banAgentHash && banAgentHash . archived === false ) { await systemService . createSkippedItem ( { type : TYPE_EMAIL , uuid : banAgentHash . uuid , value : email , } ) ; logger . info ( new Error ( `agent hash ${ agentHash } is in blocklist` ) ) ; if ( isFingerprintEnabled ) { return true ; } } } await atomService . updateMany ( { table : 'verification_code' , where : { type , email , status : VERIFICATION_CODE_STATUS . active } , data : { status : VERIFICATION_CODE_STATUS . inactive , updatedAt : new Date ( ) } , } ) ; const { code } = await userService . createVerificationCode ( { userId : viewer . id , email , type , strong : ! ! redirectUrl , } ) ; notificationService . mail . sendVerificationCode ( { to : email , type , code , redirectUrl , recipient : { displayName : user && user . displayName , } , language : viewer . language , } ) ; return true ; } ; export default resolver ;