diff --git a/src/controllers/login.controller.ts b/src/controllers/login.controller.ts index 3a7484b..363a3fe 100644 --- a/src/controllers/login.controller.ts +++ b/src/controllers/login.controller.ts @@ -7,6 +7,7 @@ import { IPlat } from 'plats/IPlat' import { PlatApple } from 'plats/PlatApple' import { PlatClient } from 'plats/PlatClient' import { PlatDiscord } from 'plats/PlatDiscord' +import { PlatEmail } from 'plats/PlatEmail' import { PlatExternalWallet } from 'plats/PlatExternalWallet' import { PlatFacebook } from 'plats/PlatFacebook' import { PlatGoogle } from 'plats/PlatGoogle' @@ -24,6 +25,7 @@ const plats: Map = new Map([ [PlatEnum.EXTERNAL_WALLET, new PlatExternalWallet()], [PlatEnum.RELAY_WALLET, new PlatExternalWallet()], [PlatEnum.DISCORD, new PlatDiscord()], + [PlatEnum.EMAIL, new PlatEmail()], ]) // 如果客户端有传入account, 则说明该次登录是绑定账号 diff --git a/src/modules/CodeRecord.ts b/src/modules/CodeRecord.ts index 737b34c..d8e1ca4 100644 --- a/src/modules/CodeRecord.ts +++ b/src/modules/CodeRecord.ts @@ -13,6 +13,7 @@ export enum CodeType { REGIST = 1, // 注册 RESET = 2, // 重置密码 VERIFY = 3, // 验证邮箱 + LOGIN = 4, // 验证码登录 } export enum CodeStatus { diff --git a/src/plats/PlatEmail.ts b/src/plats/PlatEmail.ts new file mode 100644 index 0000000..a04f55a --- /dev/null +++ b/src/plats/PlatEmail.ts @@ -0,0 +1,49 @@ +import { ZError } from 'zutils' +import { IPlat } from './IPlat' +import { CodeRecord, CodeStatus, CodeType } from 'modules/CodeRecord' +import { sha1 } from 'zutils/utils/security.util' + +const isEmail = (email: string) => { + const reg = /^(\w-*\.*)+@(\w-?)+(\.\w{2,})+$/ + return reg.test(email) +} + +const isCode = (code: string) => { + const reg = /^\d{6}$/ + return reg.test(code) +} + +export class PlatEmail implements IPlat { + async verifyToken(req: any): Promise { + let { code, token } = req.params + code = code || token + try { + JSON.parse(code) + } catch (err) { + throw new ZError(20, 'error parse token') + } + let payload = JSON.parse(code) + let { email, password } = payload + if (!email || !password || !isEmail(email) || !isCode(password)) { + throw new ZError(10, 'params mismatch') + } + let recordCode = await CodeRecord.findByEmail(email, CodeType.LOGIN) + if (!recordCode) { + throw new ZError(11, 'code not exists') + } + if (recordCode.status !== CodeStatus.PENDING) { + throw new ZError(13, 'code expired') + } + if (recordCode.code !== password) { + throw new ZError(14, 'code error') + } + const openId = sha1(email) + let data: any = { email, emailVerified: true } + const { api_platform } = req.headers + if (api_platform) { + data.platform = api_platform + } + + return { openId, data } + } +} diff --git a/src/service/email.svr.ts b/src/service/email.svr.ts index 3580641..f0e6a6d 100644 --- a/src/service/email.svr.ts +++ b/src/service/email.svr.ts @@ -34,6 +34,15 @@ export const DEFAULT_VERIFY_MAIL_HTML = `

Use it to login CEBG. Never share this code with anyone.

` +export const DEFAULT_LOGIN_MAIL_SUBJECT = 'Counter Fire email login code' +export const DEFAULT_LOGIN_MAIL_HTML = ` +

Your Counter Fire email login code is

+

{{ocde}}

+

{{time}}

+

This is your one time code that expires in 5 minutes.

+

Use it to login Counter Fire. Never share this code with anyone.

+` + export interface IMailData { from?: string to: string