feat: login change password step

pull/6775/head
NGPixel 1 year ago
parent fe8066c8f4
commit c5a441c946
No known key found for this signature in database
GPG Key ID: B755FB6870B30F63

@ -735,8 +735,7 @@ export async function up (knex) {
auth: {
[authModuleId]: {
password: await bcrypt.hash(process.env.ADMIN_PASS || '12345678', 12),
mustChangePwd: false, // TODO: Revert to true (below) once change password flow is implemented
// mustChangePwd: !process.env.ADMIN_PASS,
mustChangePwd: !process.env.ADMIN_PASS,
restrictLogin: false,
tfaIsActive: false,
tfaRequired: false,

@ -283,42 +283,42 @@ export default {
return generateError(err)
}
},
async changePassword (obj, args, context) {
try {
if (!context.req.user || context.req.user.id < 1 || context.req.user.id === 2) {
throw new WIKI.Error.AuthRequired()
}
const usr = await WIKI.db.users.query().findById(context.req.user.id)
if (!usr.isActive) {
throw new WIKI.Error.AuthAccountBanned()
}
if (!usr.isVerified) {
throw new WIKI.Error.AuthAccountNotVerified()
}
if (usr.providerKey !== 'local') {
throw new WIKI.Error.AuthProviderInvalid()
}
try {
await usr.verifyPassword(args.current)
} catch (err) {
throw new WIKI.Error.AuthPasswordInvalid()
}
await WIKI.db.users.updateUser({
id: usr.id,
newPassword: args.new
})
const newToken = await WIKI.db.users.refreshToken(usr)
return {
responseResult: generateSuccess('Password changed successfully'),
jwt: newToken.token
}
} catch (err) {
return generateError(err)
}
},
// async changePassword (obj, args, context) {
// try {
// if (!context.req.user || context.req.user.id < 1 || context.req.user.id === 2) {
// throw new WIKI.Error.AuthRequired()
// }
// const usr = await WIKI.db.users.query().findById(context.req.user.id)
// if (!usr.isActive) {
// throw new WIKI.Error.AuthAccountBanned()
// }
// if (!usr.isVerified) {
// throw new WIKI.Error.AuthAccountNotVerified()
// }
// if (usr.providerKey !== 'local') {
// throw new WIKI.Error.AuthProviderInvalid()
// }
// try {
// await usr.verifyPassword(args.current)
// } catch (err) {
// throw new WIKI.Error.AuthPasswordInvalid()
// }
// await WIKI.db.users.updateUser({
// id: usr.id,
// newPassword: args.new
// })
// const newToken = await WIKI.db.users.refreshToken(usr)
// return {
// responseResult: generateSuccess('Password changed successfully'),
// jwt: newToken.token
// }
// } catch (err) {
// return generateError(err)
// }
// },
/**
* UPLOAD USER AVATAR
*/

@ -1129,6 +1129,7 @@
"auth.changePwd.newPasswordVerify": "Verify New Password",
"auth.changePwd.proceed": "Change Password",
"auth.changePwd.subtitle": "Choose a new password",
"auth.changePwd.success": "Password updated successfully.",
"auth.enterCredentials": "Enter your credentials",
"auth.errors.forgotPassword": "Missing or invalid email address.",
"auth.errors.invalidEmail": "Email is invalid.",
@ -1198,6 +1199,7 @@
"auth.tfaSetupInstrSecond": "Enter the security code generated from your trusted device:",
"auth.tfaSetupTitle": "Your administrator has required Two-Factor Authentication (2FA) to be enabled on your account.",
"auth.tfaSetupVerifying": "Verifying...",
"auth.tfaSetupSuccess": "2FA enabled successfully on your account.",
"common.actions.activate": "Activate",
"common.actions.add": "Add",
"common.actions.apply": "Apply",

@ -66,14 +66,14 @@ export class UserKey extends Model {
await WIKI.db.userKeys.query().deleteById(res.id)
}
if (DateTime.utc() > DateTime.fromISO(res.validUntil)) {
throw new WIKI.Error.AuthValidationTokenInvalid()
throw new Error('ERR_EXPIRED_VALIDATION_TOKEN')
}
return {
...res.meta,
user: res.user
}
} else {
throw new WIKI.Error.AuthValidationTokenInvalid()
throw new Error('ERR_INVALID_VALIDATION_TOKEN')
}
}

@ -325,7 +325,10 @@ export class User extends Model {
try {
const tfaToken = await WIKI.db.userKeys.generateToken({
kind: 'tfa',
userId: user.id
userId: user.id,
meta: {
strategyId
}
})
return {
nextAction: 'provideTfa',
@ -341,7 +344,10 @@ export class User extends Model {
const tfaQRImage = await user.generateTFA(strategyId, siteId)
const tfaToken = await WIKI.db.userKeys.generateToken({
kind: 'tfaSetup',
userId: user.id
userId: user.id,
meta: {
strategyId
}
})
return {
nextAction: 'setupTfa',
@ -361,7 +367,10 @@ export class User extends Model {
try {
const pwdChangeToken = await WIKI.db.userKeys.generateToken({
kind: 'changePwd',
userId: user.id
userId: user.id,
meta: {
strategyId
}
})
return {
@ -435,11 +444,16 @@ export class User extends Model {
*/
static async loginTFA ({ strategyId, siteId, securityCode, continuationToken, setup }, context) {
if (securityCode.length === 6 && continuationToken.length > 1) {
const { user } = await WIKI.db.userKeys.validateToken({
const { user, strategyId: expectedStrategyId } = await WIKI.db.userKeys.validateToken({
kind: setup ? 'tfaSetup' : 'tfa',
token: continuationToken,
skipDelete: setup
})
if (strategyId !== expectedStrategyId) {
throw new Error('ERR_UNEXPECTED_STRATEGY_ID')
}
if (user) {
if (user.verifyTFA(strategyId, securityCode)) {
if (setup) {
@ -447,40 +461,39 @@ export class User extends Model {
}
return WIKI.db.users.afterLoginChecks(user, strategyId, context, { siteId, skipTFA: true })
} else {
throw new WIKI.Error.AuthTFAFailed()
throw new Error('ERR_INCORRECT_TFA_TOKEN')
}
}
}
throw new WIKI.Error.AuthTFAInvalid()
throw new Error('ERR_INVALID_TFA_REQUEST')
}
/**
* Change Password from a Mandatory Password Change after Login
*/
static async loginChangePassword ({ continuationToken, newPassword }, context) {
if (!newPassword || newPassword.length < 6) {
throw new WIKI.Error.InputInvalid('Password must be at least 6 characters!')
static async loginChangePassword ({ strategyId, siteId, continuationToken, newPassword }, context) {
if (!newPassword || newPassword.length < 8) {
throw new Error('ERR_PASSWORD_TOO_SHORT')
}
const usr = await WIKI.db.userKeys.validateToken({
const { user, strategyId: expectedStrategyId } = await WIKI.db.userKeys.validateToken({
kind: 'changePwd',
token: continuationToken
})
if (usr) {
await WIKI.db.users.query().patch({
password: newPassword,
mustChangePwd: false
}).findById(usr.id)
if (strategyId !== expectedStrategyId) {
throw new Error('ERR_UNEXPECTED_STRATEGY_ID')
}
return new Promise((resolve, reject) => {
context.req.logIn(usr, { session: false }, async err => {
if (err) { return reject(err) }
const jwtToken = await WIKI.db.users.refreshToken(usr)
resolve({ jwt: jwtToken.token })
})
if (user) {
user.auth[strategyId].password = await bcrypt.hash(newPassword, 12),
user.auth[strategyId].mustChangePwd = false
await user.$query().patch({
auth: user.auth
})
return WIKI.db.users.afterLoginChecks(user, strategyId, context, { siteId, skipChangePwd: true, skipTFA: true })
} else {
throw new WIKI.Error.UserNotFound()
throw new Error('ERR_INVALID_USER')
}
}

@ -21,18 +21,20 @@ export default {
if (user) {
const authStrategyData = user.auth[strategyId]
if (!authStrategyData) {
throw new WIKI.Error.AuthLoginFailed()
throw new Error('ERR_INVALID_STRATEGY_ID')
} else if (await bcrypt.compare(uPassword, authStrategyData.password) !== true) {
throw new WIKI.Error.AuthLoginFailed()
throw new Error('ERR_AUTH_FAILED')
} else if (!user.isActive) {
throw new WIKI.Error.AuthAccountBanned()
throw new Error('ERR_INACTIVE_USER')
} else if (authStrategyData.restrictLogin) {
throw new Error('ERR_LOGIN_RESTRICTED')
} else if (!user.isVerified) {
throw new WIKI.Error.AuthAccountNotVerified()
throw new Error('ERR_USER_NOT_VERIFIED')
} else {
done(null, user)
}
} else {
throw new WIKI.Error.AuthLoginFailed()
throw new Error('ERR_AUTH_FAILED')
}
} catch (err) {
done(err, null)

@ -620,6 +620,11 @@ async function forgotPassword () {
if (!isFormValid) {
throw new Error(t('auth.errors.forgotPassword'))
}
// TODO: Implement forgot password
$q.notify({
type: 'negative',
message: 'Not implemented yet.'
})
} catch (err) {
$q.notify({
type: 'negative',
@ -695,17 +700,13 @@ async function changePwd () {
const resp = await APOLLO_CLIENT.mutate({
mutation: gql`
mutation (
$userId: UUID!
$continuationToken: String
$currentPassword: String
$newPassword: String!
$strategyId: UUID!
$siteId: UUID
) {
changePassword (
userId: $userId
continuationToken: $continuationToken
currentPassword: $currentPassword
newPassword: $newPassword
strategyId: $strategyId
siteId: $siteId
@ -715,9 +716,7 @@ async function changePwd () {
message
}
jwt
mustChangePwd
mustProvideTFA
mustSetupTFA
nextAction
continuationToken
redirect
tfaQRImage
@ -725,19 +724,21 @@ async function changePwd () {
}
`,
variables: {
userId: userStore.id,
continuationToken: state.continuationToken,
currentPassword: state.password,
newPassword: state.newPassword,
strategyId: state.selectedStrategyId,
siteId: siteStore.id
}
})
if (resp.data?.login?.operation?.succeeded) {
if (resp.data?.changePassword?.operation?.succeeded) {
state.password = ''
await handleLoginResponse(resp.data.login)
$q.notify({
type: 'positive',
message: t('auth.changePwd.success')
})
await handleLoginResponse(resp.data.changePassword)
} else {
throw new Error(resp.data?.login?.operation?.message || t('auth.errors.loginError'))
throw new Error(resp.data?.changePassword?.operation?.message || t('auth.errors.loginError'))
}
} catch (err) {
$q.notify({
@ -855,6 +856,10 @@ async function finishSetupTFA () {
if (resp.data?.loginTFA?.operation?.succeeded) {
state.continuationToken = ''
state.securityCode = ''
$q.notify({
type: 'positive',
message: t('auth.tfaSetupSuccess')
})
await handleLoginResponse(resp.data.loginTFA)
} else {
throw new Error(resp.data?.loginTFA?.operation?.message || t('auth.errors.loginError'))

Loading…
Cancel
Save