
Fix not telling the user about invalid totp passcodes when logging in Add disabling totp authentication Add totp passcode when logging in Add totp settings Add general post method function Co-authored-by: kolaente <k@knt.li> Reviewed-on: https://kolaente.dev/vikunja/frontend/pulls/109
236 lines
7.8 KiB
Vue
236 lines
7.8 KiB
Vue
<template>
|
|
<div class="loader-container" :class="{ 'is-loading': passwordUpdateService.loading || emailUpdateService.loading || totpService.loading }">
|
|
<div class="card">
|
|
<header class="card-header">
|
|
<p class="card-header-title">
|
|
Update Your Password
|
|
</p>
|
|
</header>
|
|
<div class="card-content">
|
|
<div class="content">
|
|
<form @submit.prevent="updatePassword()">
|
|
<div class="field">
|
|
<label class="label" for="newPassword">New Password</label>
|
|
<div class="control">
|
|
<input class="input" type="password" id="newPassword" placeholder="The new password..."
|
|
v-model="passwordUpdate.newPassword" @keyup.enter="updatePassword"/>
|
|
</div>
|
|
</div>
|
|
<div class="field">
|
|
<label class="label" for="newPasswordConfirm">New Password Confirmation</label>
|
|
<div class="control">
|
|
<input class="input" type="password" id="newPasswordConfirm" placeholder="Confirm your new password..."
|
|
v-model="passwordConfirm" @keyup.enter="updatePassword"/>
|
|
</div>
|
|
</div>
|
|
<div class="field">
|
|
<label class="label" for="currentPassword">Current Password</label>
|
|
<div class="control">
|
|
<input class="input" type="password" id="currentPassword" placeholder="Your current password"
|
|
v-model="passwordUpdate.oldPassword" @keyup.enter="updatePassword"/>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
|
|
<div class="bigbuttons">
|
|
<button @click="updatePassword()" class="button is-primary is-fullwidth"
|
|
:class="{ 'is-loading': passwordUpdateService.loading}">
|
|
Save
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card">
|
|
<header class="card-header">
|
|
<p class="card-header-title">
|
|
Update Your E-Mail Address
|
|
</p>
|
|
</header>
|
|
<div class="card-content">
|
|
<div class="content">
|
|
<form @submit.prevent="updateEmail()">
|
|
<div class="field">
|
|
<label class="label" for="newEmail">New Email Address</label>
|
|
<div class="control">
|
|
<input class="input" type="email" id="newEmail" placeholder="The new email address..."
|
|
v-model="emailUpdate.newEmail" @keyup.enter="updateEmail"/>
|
|
</div>
|
|
</div>
|
|
<div class="field">
|
|
<label class="label" for="currentPassword">Current Password</label>
|
|
<div class="control">
|
|
<input class="input" type="password" id="currentPassword" placeholder="Your current password"
|
|
v-model="emailUpdate.password" @keyup.enter="updateEmail"/>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
|
|
<div class="bigbuttons">
|
|
<button @click="updateEmail()" class="button is-primary is-fullwidth"
|
|
:class="{ 'is-loading': emailUpdateService.loading}">
|
|
Save
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card">
|
|
<header class="card-header">
|
|
<p class="card-header-title">
|
|
Two Factor Authentication
|
|
</p>
|
|
</header>
|
|
<div class="card-content">
|
|
<a class="button is-primary" v-if="!totpEnrolled && totp.secret === ''" @click="totpEnroll()" :class="{ 'is-loading': totpService.loading }">Enroll</a>
|
|
<div class="content" v-else-if="totp.secret !== '' && !totp.enabled">
|
|
<p>
|
|
To finish your setup, use this secret in your totp app (Google Authenticator or similar): <strong>{{ totp.secret }}</strong><br/>
|
|
After that, enter a code from your app below.
|
|
</p>
|
|
<p>
|
|
Alternatively you can scan this QR code:<br/>
|
|
<img :src="totpQR" alt=""/>
|
|
</p>
|
|
<div class="field">
|
|
<label class="label" for="totpConfirmPasscode">Passcode</label>
|
|
<div class="control">
|
|
<input class="input" type="text" id="totpConfirmPasscode" placeholder="A code generated by your totp application"
|
|
v-model="totpConfirmPasscode" @keyup.enter="totpConfirm()"/>
|
|
</div>
|
|
</div>
|
|
<a class="button is-primary" @click="totpConfirm()">Confirm</a>
|
|
</div>
|
|
<div class="content" v-else-if="totp.secret !== '' && totp.enabled">
|
|
<p>
|
|
You've sucessfully set up two factor authentication!
|
|
</p>
|
|
<p v-if="!totpDisableForm">
|
|
<a class="button is-danger" @click="totpDisableForm = true">Disable</a>
|
|
</p>
|
|
<div v-if="totpDisableForm">
|
|
<div class="field">
|
|
<label class="label" for="currentPassword">Please Enter Your Password</label>
|
|
<div class="control">
|
|
<input class="input" type="password" id="currentPassword" placeholder="Your current password"
|
|
v-model="totpDisablePassword" @keyup.enter="totpDisable" v-focus/>
|
|
</div>
|
|
</div>
|
|
<a class="button is-danger" @click="totpDisable()">Disable two factor authentication</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import PasswordUpdateModel from '../../models/passwordUpdate'
|
|
import PasswordUpdateService from '../../services/passwordUpdateService'
|
|
import EmailUpdateService from '../../services/emailUpdate'
|
|
import EmailUpdateModel from '../../models/emailUpdate'
|
|
import TotpModel from '../../models/totp'
|
|
import TotpService from '../../services/totp'
|
|
|
|
export default {
|
|
name: 'Settings',
|
|
data() {
|
|
return {
|
|
passwordUpdateService: PasswordUpdateService,
|
|
passwordUpdate: PasswordUpdateModel,
|
|
passwordConfirm: '',
|
|
|
|
emailUpdateService: EmailUpdateService,
|
|
emailUpdate: EmailUpdateModel,
|
|
|
|
totpService: TotpService,
|
|
totp: TotpModel,
|
|
totpQR: '',
|
|
totpEnrolled: false,
|
|
totpConfirmPasscode: '',
|
|
totpDisableForm: false,
|
|
totpDisablePassword: '',
|
|
}
|
|
},
|
|
created() {
|
|
this.passwordUpdateService = new PasswordUpdateService()
|
|
this.passwordUpdate = new PasswordUpdateModel()
|
|
|
|
this.emailUpdateService = new EmailUpdateService()
|
|
this.emailUpdate = new EmailUpdateModel()
|
|
|
|
this.totpService = new TotpService()
|
|
this.totp = new TotpModel()
|
|
|
|
this.totpService.get()
|
|
.then(r => {
|
|
this.$set(this, 'totp', r)
|
|
this.totpSetQrCode()
|
|
})
|
|
.catch(e => {
|
|
// Error code 1016 means totp is not enabled, we don't need an error in that case.
|
|
if (e.response && e.response.data && e.response.data.code && e.response.data.code === 1016) {
|
|
this.totpEnrolled = false
|
|
return
|
|
}
|
|
|
|
this.error(e, this)
|
|
})
|
|
},
|
|
methods: {
|
|
updatePassword() {
|
|
if (this.passwordConfirm !== this.passwordUpdate.newPassword) {
|
|
this.error({message: 'The new password and its confirmation don\'t match.'}, this)
|
|
return
|
|
}
|
|
|
|
this.passwordUpdateService.update(this.passwordUpdate)
|
|
.then(() => {
|
|
this.success({message: 'The password was successfully updated.'}, this)
|
|
})
|
|
.catch(e => this.error(e, this))
|
|
},
|
|
updateEmail() {
|
|
this.emailUpdateService.update(this.emailUpdate)
|
|
.then(() => {
|
|
this.success({message: 'Your email address was successfully updated. We\'ve sent you a link to confirm it.'}, this)
|
|
})
|
|
.catch(e => this.error(e, this))
|
|
},
|
|
totpSetQrCode() {
|
|
this.totpService.qrcode()
|
|
.then(qr => {
|
|
const urlCreator = window.URL || window.webkitURL
|
|
this.totpQR = urlCreator.createObjectURL(qr)
|
|
})
|
|
},
|
|
totpEnroll() {
|
|
this.totpService.enroll()
|
|
.then(r => {
|
|
this.totpEnrolled = true
|
|
this.$set(this, 'totp', r)
|
|
this.totpSetQrCode()
|
|
})
|
|
.catch(e => this.error(e, this))
|
|
},
|
|
totpConfirm() {
|
|
this.totpService.enable({passcode: this.totpConfirmPasscode})
|
|
.then(() => {
|
|
this.$set(this.totp, 'enabled', true)
|
|
this.success({message: 'You\'ve successfully confirmed your totp setup and can use it from now on!'}, this)
|
|
})
|
|
.catch(e => this.error(e, this))
|
|
},
|
|
totpDisable() {
|
|
this.totpService.disable({password: this.totpDisablePassword})
|
|
.then(() => {
|
|
this.totpEnrolled = false
|
|
this.$set(this, 'totp', new TotpModel())
|
|
this.success({message: 'Two factor authentication was sucessfully disabled.'}, this)
|
|
})
|
|
.catch(e => this.error(e, this))
|
|
},
|
|
},
|
|
}
|
|
</script>
|