인증 코드 기반 가입을 구축하는 방법
해결책
사용자의 이메일로 전송된 임의의 4자리 숫자를 포함하는 확인 코드를 생성합니다. 앱은 확인 페이지에 코드를 입력하도록 요청하고 일단 승인되면 계정이 생성됩니다.
레시피
노드가 있는 서버 측
const randomCode = Math.floor(100000 + Math.random() * 900000).toString()
const hash = await bcrypt.hash(randomCode, Number(10))
Check bcrypt library at https://www.npmjs.com/package/bcrypt used for the encryption.
await new Token({
emailId: email,
token: hash,
createdAt: Date.now(),
}).save()
DB 예시
const schema = new Schema({
emailId: {
type: String,
},
token: {
type: String,
required: true,
},
createdAt: {
type: Date,
expires: 3600,
default: Date.now,
},
})
이메일을 보내십시오.
const emailOptions = {
subject: 'CoNectar: Verify Your Account',
data: {
verification_code: randomCode,
name: name,
},
toAddresses: [email],
fromAddress: process.env.AWS_SES_SENDER || '',
templateUrl: path.join(__dirname, '..', 'templates', 'verification-code.html'),
}
const sendEmailResponse = await emailService.send(emailOptions)
메일편에서
let AWS = require('aws-sdk')
AWS.config.update({
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
region: process.env.AWS_REGION,
})
const SES = new AWS.SES({ region: process.env.AWS_REGION })
async function getTemplate(templateUrl) {
return fs.readFileSync(templateUrl, 'utf8')
}
function buildList(listName, list, template) {
let newTemplate = template
const startTag = `{{${listName}}}`
const valueTag = `{{${listName}-value}}`
const endTag = `{{${listName}-end}}`
const startTagPos = newTemplate.indexOf(startTag)
if (startTagPos === -1) return template
const contentEndPos = newTemplate.indexOf(endTag)
if (contentEndPos === -1) return template
const contentStartPos = startTagPos + startTag.length
const endTagPos = contentEndPos + endTag.length
const content = newTemplate.slice(contentStartPos, contentEndPos)
let expandedContent = ''
list.map((value) => (expandedContent += content.replace(valueTag, value)))
newTemplate = newTemplate.slice(0, startTagPos) + expandedContent + newTemplate.slice(endTagPos)
return newTemplate
}
function transformContent(content, data) {
if (!content) return ''
for (let key in data) {
if (data.hasOwnProperty(key)) {
if (Array.isArray(data[key])) {
content = buildList(key, data[key], content)
continue
}
const replacer = `[[${key}]]`
const value = `${data[key]}`
content = content ? content.replace(replacer, value) : ''
}
}
return content
}
> 참고: Amazon SES는 값이 정의되지 않은 것을 좋아하지 않으므로 값이 정의되지 않은 경우 필드를 전혀 보내지 않거나 최소한 빈 문자열을 보냅니다.
async function send(options) {
let template, htmlBody
if (!options.textOnly) {
template = options.template || (await getTemplate(options.templateUrl))
htmlBody = options.data ? transformContent(template, options.data) : template
}
const plaintext = options.data
? transformContent(options.plaintext, options.data)
: options.plaintext || ''
let params = {
Destination: {
ToAddresses: options.toAddresses,
},
Message: {
Body: {
...(options.textOnly
? {
Text: {
Charset: 'UTF-8',
Data: plaintext,
},
}
: {
Html: {
Charset: 'UTF-8',
Data: htmlBody,
},
}),
},
Subject: {
Charset: 'UTF-8',
Data: options.subject,
},
},
Source: options.fromAddress || process.env.CDP_SENDER_EMAIL,
}
return SES.sendEmail(params).promise()
}
if (!sendEmailResponse || !sendEmailResponse.MessageId) {
throw Boom.conflict('Could not send email')
}
React를 사용하는 클라이언트 측
계정을 만드는 데 필요한 정보가 포함된 양식이 포함된 가입 페이지를 만들고 위치 및 기록 기능을 사용하여 정보를 보냅니다.
let userPayload = {
name: userLogin.name.value,
username: userLogin.username.value,
email: userLogin.email.value,
password: userLogin.password.value,
photo: profileImage && profileImage instanceof File ? profileImage : null,
}
history.push({ pathname: '/verify-code', state: { ...userPayload } })
Sign up example Preview
NOTE: Read reactrouter documentation https://v5.reactrouter.com/web/api/history
verifyCode 반응 구성 요소를 만들고 위치에서 정보를 가져옵니다.
const history = useHistory()
const location = useLocation()
const [verificationCode, setVerificationCode] = useState('') // Needed to store the code
const [email, setEmail] = useState('')
const [name, setName] = useState('')
const [payload, setIsPayload] = useState({})
useEffect 아래는 위치에서 정보가 있는 경우 로드하고, 정보가 없는 경우 페이지가 리디렉션됩니다.
useEffect(() => {
if (
!location.state ||
!location.state.email ||
!location.state.name ||
!location.state.username ||
!location.state.password
) {
history.push('/')
} else {
setEmail(location.state.email)
setName(location.state.name)
setIsPayload(location.state)
}
}, [location, history])
확인 코드를 채우는 데 필요한 양식을 만듭니다.
Note: we use react-hook-form to handle the verification form, see https://react-hook-form.com/ for reference.
const {
handleSubmit,
reset,
formState: { isSubmitting },
} = useForm()
Note: We are using some features from ChakraUI, see the documentation below:
https://chakra-ui.com/guides/first-steps
Imported: FormControl, Center, useToast, PinInput, PinInputField.Note: We are using some features from TailwindCSS, see the documentation below:
https://tailwindcss.com/docs/installation
양식 사용을 위한 JSX 구성 요소, PinInput을 사용하여 코드 값을 검색합니다.
return (
<div className="flex flex-1 justify-center items-center h-full w-full">
<div className="flex flex-col w-full max-w-md px-4 py-8 bg-white rounded-lg shadow-2xl dark:bg-gray-800 sm:px-6 md:px-8 lg:px-10">
<div className="self-center mb-2 text-4xl font-medium text-gray-600 sm:text-3xl dark:text-white">
Verification code
</div>
<div className="self-center mb-4 text-sm font-medium text-gray-400 dark:text-white">
Please check your email for the verification code.
</div>
<div className="my-4">
<form onSubmit={handleSubmit(onSubmit)} action="#" autoComplete="off">
<FormControl>
<div className="flex flex-col mb-6">
<div className="flex-auto mb-2">
<Center>
<PinInput
value={verificationCode}
onChange={handleChange}
className="flex-auto"
>
<PinInputField className="text-gray-600" />
<PinInputField className="text-gray-600" />
<PinInputField className="text-gray-600" />
<PinInputField className="text-gray-600" />
<PinInputField className="text-gray-600" />
<PinInputField className="text-gray-600" />
</PinInput>
</Center>
</div>
</div>
</FormControl>
<div className={'flex w-full'}>
<Button
disabled={verificationCode.length < 6}
text="Verify"
isLoading={isSubmitting}
type="submit"
/>
</div>
</form>
</div>
<div className="my-4">
<div className="flex w-full">
<p className="text-sm font-medium text-gray-600">
Didn't receive the code?
<button
onClick={() => onRequestCode(true)}
className="text-purple-600 hover:text-purple-800 focus:text-gray-600"
>
click here to request a new code
</button>
</p>
</div>
</div>
</div>
</div>
)
Verification code example Preview
UseToast 에 대한 참조를 생성하면 이 chakraUI 기능을 통해 오류를 쉽게 처리할 수 있습니다.
const toast = useToast()
서버, onRequestCode(코드를 요청하고 사용자의 이메일로 전송됨) 및 onSubmit(코드가 일치하는 경우 새 계정이 생성됨)에서 정보를 검색하는 나머지 기능을 만듭니다.
const onRequestCode = useCallback(
async (forceSend = false) => {
try {
if (email && name) {
const response = await requestVerificationCode({
email: email,
name: name,
forceSend: forceSend,
})
if (response.statusText === 'Created') {
toast({
duration: 5000,
status: 'success',
position: 'top-right',
variant: 'left-accent',
title: 'SUCCESS: Check your email for the verification code',
description: 'Please check your inbox messages.',
})
} else if (response.status === 400) {
toast({
duration: 5000,
status: 'error',
position: 'top-right',
variant: 'left-accent',
title: 'WARNING: Verification code already sent',
description: 'Please check your email or try again later.',
})
}
}
} catch (error) {
toast({
duration: 5000,
status: 'error',
position: 'top-right',
variant: 'left-accent',
title: 'ERROR: Oops! Something Went Wrong',
description: 'Please contact support at [email protected]',
})
} finally {
reset()
}
},
[email, name, toast, reset]
)
이 기능은 "requestVerificationCode"라는 서비스를 의미하며 서버에 코드를 요청하고 참조된 이메일 주소로 전송되는 것을 의미합니다.
이는 "forceSend"라는 값을 호출합니다. 이를 통해 서버는 기본적으로 5분마다 코드를 보낼 수만 있기 때문에 페이지가 "true"로 설정된 작업을 통해 코드를 요청할 수 있습니다.
오류 처리에 주의하십시오. 서버의 응답과 일치해야 합니다.
이 함수는 로드당 한 번 useEffect에 의해 호출되므로 useCallback을 사용하여 함수를 콜백으로 설정하는 것이 좋습니다.
useEffect(() => {
onRequestCode(false)
}, [onRequestCode])
const onSubmit = async (data) => {
try {
const response = await tokenVerificationCode({
email,
verificationCode,
})
if (response.data?.checkCode) {
toast({
duration: 5000,
status: 'success',
position: 'top-right',
variant: 'left-accent',
title: 'SUCCESS: your verification code has been verified',
})
onSignUp()
}
} catch (error) {
reset()
if (error.response.data.statusCode === 400) {
toast({
duration: 5000,
status: 'error',
position: 'top-right',
variant: 'left-accent',
title: 'ERROR: Invalid or expired verification code',
})
}
}
}
이 "onSubmit"기능은 코드가 서버의 코드와 일치하는지 확인하는 서비스를 사용합니다. 일치하는 경우 이제 아래 "onSignUp"기능으로 전달됩니다.
const onSignUp = async () => {
try {
const response = await signup(payload)
if (response.ok) {
history.push({ pathname: '/login' })
toast({
duration: 5000,
status: 'success',
position: 'top-right',
variant: 'left-accent',
title: 'SUCCESS: Your account has been created',
description: 'Please login.',
})
} else {
toast({
duration: 5000,
status: 'error',
position: 'top-right',
variant: 'left-accent',
title: 'ERROR: Email is already in use!',
description: 'Please contact support at [email protected]',
})
history.push({ pathname: '/login' })
}
} catch (error) {
toast({
duration: 5000,
status: 'error',
position: 'top-right',
variant: 'left-accent',
title: 'ERROR: Oops! Something Went Wrong',
description: error.message + ', Please contact support at [email protected]',
})
} finally {
reset()
}
}
이 "onSignUp"기능은 존재하지 않는 경우 새 계정을 생성합니다.
useEffect(() => {
return () => {
reset()
location.state = null
}
}, [location, reset])
Reference
이 문제에 관하여(인증 코드 기반 가입을 구축하는 방법), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/icchatechnologies/how-to-build-a-verification-code-based-sign-up-3fka텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)