Commit ae733392 authored by NGPixel's avatar NGPixel

feat: password reset

parent f98a07ec
......@@ -253,6 +253,10 @@ export default {
hideLocal: {
type: Boolean,
default: false
},
changePwdContinuationToken: {
type: String,
default: null
}
},
data () {
......@@ -309,6 +313,9 @@ export default {
},
selectedStrategyKey (newValue, oldValue) {
this.selectedStrategy = _.find(this.strategies, ['key', newValue])
if (this.screen === 'changePwd') {
return
}
this.screen = 'login'
if (!this.selectedStrategy.strategy.useForm) {
this.isLoading = true
......@@ -322,6 +329,10 @@ export default {
},
mounted () {
this.isShown = true
if (this.changePwdContinuationToken) {
this.screen = 'changePwd'
this.continuationToken = this.changePwdContinuationToken
}
},
methods: {
/**
......@@ -475,32 +486,51 @@ export default {
this.loaderColor = 'grey darken-4'
this.loaderTitle = this.$t('auth:changePwd.loading')
this.isLoading = true
const resp = await this.$apollo.mutate({
mutation: gql`
{
authentication {
activeStrategies {
key
try {
const resp = await this.$apollo.mutate({
mutation: gql`
mutation (
$continuationToken: String!
$newPassword: String!
) {
authentication {
loginChangePassword (
continuationToken: $continuationToken
newPassword: $newPassword
) {
responseResult {
succeeded
errorCode
slug
message
}
jwt
continuationToken
redirect
}
}
}
`,
variables: {
continuationToken: this.continuationToken,
newPassword: this.newPassword
}
`,
variables: {
continuationToken: this.continuationToken,
newPassword: this.newPassword
})
if (_.has(resp, 'data.authentication.loginChangePassword')) {
let respObj = _.get(resp, 'data.authentication.loginChangePassword', {})
if (respObj.responseResult.succeeded === true) {
this.handleLoginResponse(respObj)
} else {
throw new Error(respObj.responseResult.message)
}
} else {
throw new Error(this.$t('auth:genericError'))
}
})
if (_.get(resp, 'data.authentication.loginChangePassword.responseResult.succeeded', false) === true) {
this.loaderColor = 'green darken-1'
this.loaderTitle = this.$t('auth:loginSuccess')
Cookies.set('jwt', _.get(resp, 'data.authentication.loginChangePassword.jwt', ''), { expires: 365 })
_.delay(() => {
window.location.replace('/') // TEMPORARY - USE RETURNURL
}, 1000)
} else {
} catch (err) {
console.error(err)
this.$store.commit('showNotification', {
style: 'red',
message: _.get(resp, 'data.authentication.loginChangePassword.responseResult.message', false),
message: err.message,
icon: 'alert'
})
this.isLoading = false
......@@ -519,11 +549,57 @@ export default {
* FORGOT PASSWORD SUBMIT
*/
async forgotPasswordSubmit () {
this.$store.commit('showNotification', {
style: 'pink',
message: 'Coming soon!',
icon: 'ferry'
})
this.loaderColor = 'grey darken-4'
this.loaderTitle = this.$t('auth:forgotPasswordLoading')
this.isLoading = true
try {
const resp = await this.$apollo.mutate({
mutation: gql`
mutation (
$email: String!
) {
authentication {
forgotPassword (
email: $email
) {
responseResult {
succeeded
errorCode
slug
message
}
}
}
}
`,
variables: {
email: this.username
}
})
if (_.has(resp, 'data.authentication.forgotPassword.responseResult')) {
let respObj = _.get(resp, 'data.authentication.forgotPassword.responseResult', {})
if (respObj.succeeded === true) {
this.$store.commit('showNotification', {
style: 'success',
message: this.$t('auth:forgotPasswordSuccess'),
icon: 'email'
})
this.screen = 'login'
} else {
throw new Error(respObj.message)
}
} else {
throw new Error(this.$t('auth:genericError'))
}
} catch (err) {
console.error(err)
this.$store.commit('showNotification', {
style: 'red',
message: err.message,
icon: 'alert'
})
}
this.isLoading = false
},
handleLoginResponse (respObj) {
this.continuationToken = respObj.continuationToken
......
......@@ -149,6 +149,28 @@ router.get('/verify/:token', bruteforce.prevent, async (req, res, next) => {
})
/**
* Reset Password
*/
router.get('/login-reset/:token', bruteforce.prevent, async (req, res, next) => {
try {
const usr = await WIKI.models.userKeys.validateToken({ kind: 'resetPwd', token: req.params.token })
if (!usr) {
throw new Error('Invalid Token')
}
req.brute.reset()
const changePwdContinuationToken = await WIKI.models.userKeys.generateToken({
userId: usr.id,
kind: 'changePwd'
})
const bgUrl = !_.isEmpty(WIKI.config.auth.loginBgUrl) ? WIKI.config.auth.loginBgUrl : '/_assets/img/splash/1.jpg'
res.render('login', { bgUrl, hideLocal: WIKI.config.auth.hideLocal, changePwdContinuationToken })
} catch (err) {
next(err)
}
})
/**
* JWT Public Endpoints
*/
router.get('/.well-known/jwk.json', function (req, res, next) {
......
......@@ -56,7 +56,7 @@ module.exports = {
subject: `${opts.subject} - ${WIKI.config.title}`,
text: opts.text,
html: _.get(this.templates, opts.template)({
logo: '',
logo: WIKI.config.logoUrl,
siteTitle: WIKI.config.title,
copyright: WIKI.config.company.length > 0 ? WIKI.config.company : 'Powered by Wiki.js',
...opts.data
......
......@@ -138,6 +138,19 @@ module.exports = {
}
},
/**
* Perform Mandatory Password Change after Login
*/
async forgotPassword (obj, args, context) {
try {
await WIKI.models.users.loginForgotPassword(args, context)
return {
responseResult: graphHelper.generateSuccess('Password reset request processed.')
}
} catch (err) {
return graphHelper.generateError(err)
}
},
/**
* Register a new account
*/
async register (obj, args, context) {
......
......@@ -52,6 +52,10 @@ type AuthenticationMutation {
newPassword: String!
): AuthenticationLoginResponse @rateLimit(limit: 5, duration: 60)
forgotPassword(
email: String!
): DefaultResponse @rateLimit(limit: 3, duration: 60)
register(
email: String!
password: String!
......
......@@ -479,6 +479,38 @@ module.exports = class User extends Model {
}
/**
* Send a password reset request
*/
static async loginForgotPassword ({ email }, context) {
const usr = await WIKI.models.users.query().where({
email,
providerKey: 'local'
}).first()
if (!usr) {
WIKI.logger.debug(`Password reset attempt on nonexistant local account ${email}: [DISCARDED]`)
return
}
const resetToken = await WIKI.models.userKeys.generateToken({
userId: usr.id,
kind: 'resetPwd'
})
await WIKI.mail.send({
template: 'accountResetPwd',
to: email,
subject: `Password Reset Request`,
data: {
preheadertext: `A password reset was requested for ${WIKI.config.title}`,
title: `A password reset was requested for ${WIKI.config.title}`,
content: `Click the button below to reset your password. If you didn't request this password reset, simply discard this email.`,
buttonLink: `${WIKI.config.host}/login-reset/${resetToken}`,
buttonText: 'Reset Password'
},
text: `A password reset was requested for wiki ${WIKI.config.title}. Open the following link to proceed: ${WIKI.config.host}/login-reset/${resetToken}`
})
}
/**
* Create a new user
*
* @param {Object} param0 User Fields
......
......@@ -5,4 +5,5 @@ block body
login(
bg-url=bgUrl
hide-local=hideLocal
change-pwd-continuation-token=changePwdContinuationToken
)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment