Commit ae733392 authored by NGPixel's avatar NGPixel

feat: password reset

parent f98a07ec
...@@ -253,6 +253,10 @@ export default { ...@@ -253,6 +253,10 @@ export default {
hideLocal: { hideLocal: {
type: Boolean, type: Boolean,
default: false default: false
},
changePwdContinuationToken: {
type: String,
default: null
} }
}, },
data () { data () {
...@@ -309,6 +313,9 @@ export default { ...@@ -309,6 +313,9 @@ export default {
}, },
selectedStrategyKey (newValue, oldValue) { selectedStrategyKey (newValue, oldValue) {
this.selectedStrategy = _.find(this.strategies, ['key', newValue]) this.selectedStrategy = _.find(this.strategies, ['key', newValue])
if (this.screen === 'changePwd') {
return
}
this.screen = 'login' this.screen = 'login'
if (!this.selectedStrategy.strategy.useForm) { if (!this.selectedStrategy.strategy.useForm) {
this.isLoading = true this.isLoading = true
...@@ -322,6 +329,10 @@ export default { ...@@ -322,6 +329,10 @@ export default {
}, },
mounted () { mounted () {
this.isShown = true this.isShown = true
if (this.changePwdContinuationToken) {
this.screen = 'changePwd'
this.continuationToken = this.changePwdContinuationToken
}
}, },
methods: { methods: {
/** /**
...@@ -475,12 +486,27 @@ export default { ...@@ -475,12 +486,27 @@ export default {
this.loaderColor = 'grey darken-4' this.loaderColor = 'grey darken-4'
this.loaderTitle = this.$t('auth:changePwd.loading') this.loaderTitle = this.$t('auth:changePwd.loading')
this.isLoading = true this.isLoading = true
try {
const resp = await this.$apollo.mutate({ const resp = await this.$apollo.mutate({
mutation: gql` mutation: gql`
{ mutation (
$continuationToken: String!
$newPassword: String!
) {
authentication { authentication {
activeStrategies { loginChangePassword (
key continuationToken: $continuationToken
newPassword: $newPassword
) {
responseResult {
succeeded
errorCode
slug
message
}
jwt
continuationToken
redirect
} }
} }
} }
...@@ -490,17 +516,21 @@ export default { ...@@ -490,17 +516,21 @@ export default {
newPassword: this.newPassword newPassword: this.newPassword
} }
}) })
if (_.get(resp, 'data.authentication.loginChangePassword.responseResult.succeeded', false) === true) { if (_.has(resp, 'data.authentication.loginChangePassword')) {
this.loaderColor = 'green darken-1' let respObj = _.get(resp, 'data.authentication.loginChangePassword', {})
this.loaderTitle = this.$t('auth:loginSuccess') if (respObj.responseResult.succeeded === true) {
Cookies.set('jwt', _.get(resp, 'data.authentication.loginChangePassword.jwt', ''), { expires: 365 }) this.handleLoginResponse(respObj)
_.delay(() => {
window.location.replace('/') // TEMPORARY - USE RETURNURL
}, 1000)
} else { } else {
throw new Error(respObj.responseResult.message)
}
} else {
throw new Error(this.$t('auth:genericError'))
}
} catch (err) {
console.error(err)
this.$store.commit('showNotification', { this.$store.commit('showNotification', {
style: 'red', style: 'red',
message: _.get(resp, 'data.authentication.loginChangePassword.responseResult.message', false), message: err.message,
icon: 'alert' icon: 'alert'
}) })
this.isLoading = false this.isLoading = false
...@@ -519,11 +549,57 @@ export default { ...@@ -519,11 +549,57 @@ export default {
* FORGOT PASSWORD SUBMIT * FORGOT PASSWORD SUBMIT
*/ */
async forgotPasswordSubmit () { async forgotPasswordSubmit () {
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', { this.$store.commit('showNotification', {
style: 'pink', style: 'red',
message: 'Coming soon!', message: err.message,
icon: 'ferry' icon: 'alert'
}) })
}
this.isLoading = false
}, },
handleLoginResponse (respObj) { handleLoginResponse (respObj) {
this.continuationToken = respObj.continuationToken this.continuationToken = respObj.continuationToken
......
...@@ -149,6 +149,28 @@ router.get('/verify/:token', bruteforce.prevent, async (req, res, next) => { ...@@ -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 * JWT Public Endpoints
*/ */
router.get('/.well-known/jwk.json', function (req, res, next) { router.get('/.well-known/jwk.json', function (req, res, next) {
......
...@@ -56,7 +56,7 @@ module.exports = { ...@@ -56,7 +56,7 @@ module.exports = {
subject: `${opts.subject} - ${WIKI.config.title}`, subject: `${opts.subject} - ${WIKI.config.title}`,
text: opts.text, text: opts.text,
html: _.get(this.templates, opts.template)({ html: _.get(this.templates, opts.template)({
logo: '', logo: WIKI.config.logoUrl,
siteTitle: WIKI.config.title, siteTitle: WIKI.config.title,
copyright: WIKI.config.company.length > 0 ? WIKI.config.company : 'Powered by Wiki.js', copyright: WIKI.config.company.length > 0 ? WIKI.config.company : 'Powered by Wiki.js',
...opts.data ...opts.data
......
...@@ -138,6 +138,19 @@ module.exports = { ...@@ -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 * Register a new account
*/ */
async register (obj, args, context) { async register (obj, args, context) {
......
...@@ -52,6 +52,10 @@ type AuthenticationMutation { ...@@ -52,6 +52,10 @@ type AuthenticationMutation {
newPassword: String! newPassword: String!
): AuthenticationLoginResponse @rateLimit(limit: 5, duration: 60) ): AuthenticationLoginResponse @rateLimit(limit: 5, duration: 60)
forgotPassword(
email: String!
): DefaultResponse @rateLimit(limit: 3, duration: 60)
register( register(
email: String! email: String!
password: String! password: String!
......
...@@ -479,6 +479,38 @@ module.exports = class User extends Model { ...@@ -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 * Create a new user
* *
* @param {Object} param0 User Fields * @param {Object} param0 User Fields
......
...@@ -5,4 +5,5 @@ block body ...@@ -5,4 +5,5 @@ block body
login( login(
bg-url=bgUrl bg-url=bgUrl
hide-local=hideLocal 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