247 lines
5.1 KiB
JavaScript
247 lines
5.1 KiB
JavaScript
const {
|
||
findUser
|
||
} = require('./account')
|
||
const {
|
||
userCollection,
|
||
LOG_TYPE
|
||
} = require('../../common/constants')
|
||
const {
|
||
ERROR
|
||
} = require('../../common/error')
|
||
const {
|
||
logout
|
||
} = require('./logout')
|
||
const PasswordUtils = require('./password')
|
||
|
||
async function realPreLogin (params = {}) {
|
||
const {
|
||
user
|
||
} = params
|
||
const appId = this.getUniversalClientInfo().appId
|
||
const {
|
||
total,
|
||
userMatched
|
||
} = await findUser({
|
||
userQuery: user,
|
||
authorizedApp: appId
|
||
})
|
||
if (userMatched.length === 0) {
|
||
if (total > 0) {
|
||
throw {
|
||
errCode: ERROR.ACCOUNT_NOT_EXISTS_IN_CURRENT_APP
|
||
}
|
||
}
|
||
throw {
|
||
errCode: ERROR.ACCOUNT_NOT_EXISTS
|
||
}
|
||
} else if (userMatched.length > 1) {
|
||
throw {
|
||
errCode: ERROR.ACCOUNT_CONFLICT
|
||
}
|
||
}
|
||
const userRecord = userMatched[0]
|
||
checkLoginUserRecord(userRecord)
|
||
return userRecord
|
||
}
|
||
|
||
async function preLogin (params = {}) {
|
||
const {
|
||
user
|
||
} = params
|
||
try {
|
||
const user = await realPreLogin.call(this, params)
|
||
return user
|
||
} catch (error) {
|
||
await this.middleware.uniIdLog({
|
||
success: false,
|
||
data: user,
|
||
type: LOG_TYPE.LOGIN
|
||
})
|
||
throw error
|
||
}
|
||
}
|
||
|
||
async function preLoginWithPassword (params = {}) {
|
||
const {
|
||
user,
|
||
password
|
||
} = params
|
||
try {
|
||
const userRecord = await realPreLogin.call(this, params)
|
||
const {
|
||
passwordErrorLimit,
|
||
passwordErrorRetryTime
|
||
} = this.config
|
||
const {
|
||
clientIP
|
||
} = this.getUniversalClientInfo()
|
||
// 根据ip地址,密码错误次数过多,锁定登录
|
||
let loginIPLimit = userRecord.login_ip_limit || []
|
||
// 清理无用记录
|
||
loginIPLimit = loginIPLimit.filter(item => item.last_error_time > Date.now() - passwordErrorRetryTime * 1000)
|
||
let currentIPLimit = loginIPLimit.find(item => item.ip === clientIP)
|
||
if (currentIPLimit && currentIPLimit.error_times >= passwordErrorLimit) {
|
||
throw {
|
||
errCode: ERROR.PASSWORD_ERROR_EXCEED_LIMIT
|
||
}
|
||
}
|
||
const passwordUtils = new PasswordUtils({
|
||
userRecord,
|
||
clientInfo: this.getUniversalClientInfo(),
|
||
passwordSecret: this.config.passwordSecret
|
||
})
|
||
|
||
const {
|
||
success: checkPasswordSuccess,
|
||
refreshPasswordInfo
|
||
} = passwordUtils.checkUserPassword({
|
||
password
|
||
})
|
||
if (!checkPasswordSuccess) {
|
||
// 更新用户ip对应的密码错误记录
|
||
if (!currentIPLimit) {
|
||
currentIPLimit = {
|
||
ip: clientIP,
|
||
error_times: 1,
|
||
last_error_time: Date.now()
|
||
}
|
||
loginIPLimit.push(currentIPLimit)
|
||
} else {
|
||
currentIPLimit.error_times++
|
||
currentIPLimit.last_error_time = Date.now()
|
||
}
|
||
await userCollection.doc(userRecord._id).update({
|
||
login_ip_limit: loginIPLimit
|
||
})
|
||
throw {
|
||
errCode: ERROR.PASSWORD_ERROR
|
||
}
|
||
}
|
||
const extraData = {}
|
||
if (refreshPasswordInfo) {
|
||
extraData.password = refreshPasswordInfo.passwordHash
|
||
extraData.password_secret_version = refreshPasswordInfo.version
|
||
}
|
||
|
||
const currentIPLimitIndex = loginIPLimit.indexOf(currentIPLimit)
|
||
if (currentIPLimitIndex > -1) {
|
||
loginIPLimit.splice(currentIPLimitIndex, 1)
|
||
}
|
||
extraData.login_ip_limit = loginIPLimit
|
||
return {
|
||
user: userRecord,
|
||
extraData
|
||
}
|
||
} catch (error) {
|
||
await this.middleware.uniIdLog({
|
||
success: false,
|
||
data: user,
|
||
type: LOG_TYPE.LOGIN
|
||
})
|
||
throw error
|
||
}
|
||
}
|
||
|
||
function checkLoginUserRecord (user) {
|
||
switch (user.status) {
|
||
case undefined:
|
||
case 0:
|
||
break
|
||
case 1:
|
||
throw {
|
||
errCode: ERROR.ACCOUNT_BANNED
|
||
}
|
||
case 2:
|
||
throw {
|
||
errCode: ERROR.ACCOUNT_AUDITING
|
||
}
|
||
case 3:
|
||
throw {
|
||
errCode: ERROR.ACCOUNT_AUDIT_FAILED
|
||
}
|
||
case 4:
|
||
throw {
|
||
errCode: ERROR.ACCOUNT_CLOSED
|
||
}
|
||
default:
|
||
break
|
||
}
|
||
}
|
||
|
||
async function thirdPartyLogin (params = {}) {
|
||
const {
|
||
user
|
||
} = params
|
||
return {
|
||
mobileConfirmed: !!user.mobile_confirmed,
|
||
emailConfirmed: !!user.email_confirmed
|
||
}
|
||
}
|
||
|
||
async function postLogin (params = {}) {
|
||
const {
|
||
user,
|
||
extraData,
|
||
isThirdParty = false
|
||
} = params
|
||
const {
|
||
clientIP
|
||
} = this.getUniversalClientInfo()
|
||
const uniIdToken = this.getUniversalUniIdToken()
|
||
const uid = user._id
|
||
const updateData = {
|
||
last_login_date: Date.now(),
|
||
last_login_ip: clientIP,
|
||
...extraData
|
||
}
|
||
const createTokenRes = await this.uniIdCommon.createToken({
|
||
uid
|
||
})
|
||
|
||
const {
|
||
errCode,
|
||
token,
|
||
tokenExpired
|
||
} = createTokenRes
|
||
if (errCode) {
|
||
throw createTokenRes
|
||
}
|
||
|
||
if (uniIdToken) {
|
||
try {
|
||
await logout.call(this)
|
||
} catch (error) {}
|
||
}
|
||
|
||
await userCollection.doc(uid).update(updateData)
|
||
await this.middleware.uniIdLog({
|
||
data: {
|
||
user_id: uid
|
||
},
|
||
type: LOG_TYPE.LOGIN
|
||
})
|
||
return {
|
||
errCode: 0,
|
||
newToken: {
|
||
token,
|
||
tokenExpired
|
||
},
|
||
uid,
|
||
...(
|
||
isThirdParty
|
||
? thirdPartyLogin({
|
||
user
|
||
})
|
||
: {}
|
||
),
|
||
passwordConfirmed: !!user.password
|
||
}
|
||
}
|
||
|
||
module.exports = {
|
||
preLogin,
|
||
postLogin,
|
||
checkLoginUserRecord,
|
||
preLoginWithPassword
|
||
}
|