add 2fa settings

This commit is contained in:
MaysWind
2020-11-02 00:02:40 +08:00
parent 7dc628cfcc
commit 78937d16d2
12 changed files with 535 additions and 8 deletions
+41
View File
@@ -3394,6 +3394,16 @@
"integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==",
"dev": true
},
"clipboard": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.6.tgz",
"integrity": "sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg==",
"requires": {
"good-listener": "^1.2.2",
"select": "^1.1.2",
"tiny-emitter": "^2.0.0"
}
},
"clipboardy": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-2.3.0.tgz",
@@ -4387,6 +4397,11 @@
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
"dev": true
},
"delegate": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
"integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
},
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
@@ -5726,6 +5741,14 @@
"slash": "^2.0.0"
}
},
"good-listener": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
"integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
"requires": {
"delegate": "^3.1.2"
}
},
"graceful-fs": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
@@ -9453,6 +9476,11 @@
"ajv-keywords": "^3.5.2"
}
},
"select": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
"integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0="
},
"select-hose": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
@@ -10517,6 +10545,11 @@
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=",
"dev": true
},
"tiny-emitter": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
},
"tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
@@ -11020,6 +11053,14 @@
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.12.tgz",
"integrity": "sha512-uhmLFETqPPNyuLLbsKz6ioJ4q7AZHzD8ZVFNATNyICSZouqP2Sz0rotWQC8UNBF6VGSCs5abnKJoStA6JbCbfg=="
},
"vue-clipboard2": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/vue-clipboard2/-/vue-clipboard2-0.3.1.tgz",
"integrity": "sha512-H5S/agEDj0kXjUb5GP2c0hCzIXWRBygaWLN3NEFsaI9I3uWin778SFEMt8QRXiPG+7anyjqWiw2lqcxWUSfkYg==",
"requires": {
"clipboard": "^2.0.0"
}
},
"vue-eslint-parser": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-7.1.1.tgz",
+1
View File
@@ -16,6 +16,7 @@
"js-cookie": "^2.2.1",
"moment": "^2.29.1",
"vue": "^2.6.11",
"vue-clipboard2": "^0.3.1",
"vue-i18n": "^8.22.0",
"vue-i18n-filter": "^0.1.6",
"vue-moment": "^4.1.0"
+44 -7
View File
@@ -203,7 +203,29 @@ func (a *TwoFactorAuthorizationsApi) TwoFactorEnableConfirmHandler(c *core.Conte
}
func (a *TwoFactorAuthorizationsApi) TwoFactorDisableHandler(c *core.Context) (interface{}, *errs.Error) {
var disableReq models.TwoFactorDisableRequest
err := c.ShouldBindJSON(&disableReq)
if err != nil {
log.WarnfWithRequestId(c, "[twofactor_authorizations.TwoFactorDisableHandler] parse request failed, because %s", err.Error())
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
}
uid := c.GetCurrentUid()
user, err := a.users.GetUserById(uid)
if err != nil {
if !errs.IsCustomError(err) {
log.WarnfWithRequestId(c, "[twofactor_authorizations.TwoFactorDisableHandler] failed to get user for user \"uid:%d\", because %s", uid, err.Error())
}
return nil, errs.ErrUserNotFound
}
if !a.users.IsPasswordEqualsUserPassword(disableReq.Password, user) {
return nil, errs.ErrUserPasswordWrong
}
enableTwoFactor, err := a.twoFactorAuthorizations.ExistsTwoFactorSetting(uid)
if err != nil {
@@ -235,7 +257,29 @@ func (a *TwoFactorAuthorizationsApi) TwoFactorDisableHandler(c *core.Context) (i
}
func (a *TwoFactorAuthorizationsApi) TwoFactorRecoveryCodeRegenerateHandler(c *core.Context) (interface{}, *errs.Error) {
var regenerateReq models.TwoFactorRegenerateRecoveryCodeRequest
err := c.ShouldBindJSON(&regenerateReq)
if err != nil {
log.WarnfWithRequestId(c, "[twofactor_authorizations.TwoFactorRecoveryCodeRegenerateHandler] parse request failed, because %s", err.Error())
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
}
uid := c.GetCurrentUid()
user, err := a.users.GetUserById(uid)
if err != nil {
if !errs.IsCustomError(err) {
log.WarnfWithRequestId(c, "[twofactor_authorizations.TwoFactorRecoveryCodeRegenerateHandler] failed to get user for user \"uid:%d\", because %s", uid, err.Error())
}
return nil, errs.ErrUserNotFound
}
if !a.users.IsPasswordEqualsUserPassword(regenerateReq.Password, user) {
return nil, errs.ErrUserPasswordWrong
}
enableTwoFactor, err := a.twoFactorAuthorizations.ExistsTwoFactorSetting(uid)
if err != nil {
@@ -254,13 +298,6 @@ func (a *TwoFactorAuthorizationsApi) TwoFactorRecoveryCodeRegenerateHandler(c *c
return nil, errs.Or(err, errs.ErrOperationFailed)
}
user, err := a.users.GetUserById(uid)
if err != nil {
log.WarnfWithRequestId(c, "[twofactor_authorizations.TwoFactorRecoveryCodeRegenerateHandler] failed to get user for user \"uid:%d\", because %s", uid, err.Error())
return nil, errs.ErrUserNotFound
}
err = a.twoFactorAuthorizations.CreateTwoFactorRecoveryCodes(uid, recoveryCodes, user.Salt)
if err != nil {
+8
View File
@@ -25,6 +25,14 @@ type TwoFactorEnableConfirmResponse struct {
RecoveryCodes []string `json:"recoveryCodes"`
}
type TwoFactorDisableRequest struct {
Password string `json:"password" binding:"omitempty,min=6,max=128"`
}
type TwoFactorRegenerateRecoveryCodeRequest struct {
Password string `json:"password" binding:"omitempty,min=6,max=128"`
}
type TwoFactorStatusResponse struct {
Enable bool `json:"enable"`
CreatedAt int64 `json:"createdAt,omitempty"`
+22
View File
@@ -135,4 +135,26 @@ export default {
oldPassword
});
},
get2FAStatus: () => {
return axios.get('v1/users/2fa/status.json');
},
enable2FA: () => {
return axios.post('v1/users/2fa/enable/request.json');
},
confirmEnable2FA: ({ secret, passcode }) => {
return axios.post('v1/users/2fa/enable/confirm.json', {
secret,
passcode
});
},
disable2FA: ({ password }) => {
return axios.post('v1/users/2fa/disable.json', {
password
});
},
regenerate2FARecoveryCode: ({ password }) => {
return axios.post('v1/users/2fa/recovery/regenerate.json', {
password
});
},
};
+16
View File
@@ -46,6 +46,7 @@ export default {
'parameter': {
'username': 'Username',
'password': 'Password',
'passcode': 'Passcode',
'email': 'Email',
'nickname': 'Nickname',
'oldPassword': 'Current Password',
@@ -66,6 +67,11 @@ export default {
'Update': 'Update',
'Done': 'Done',
'Continue': 'Continue',
'Status': 'Status',
'Enable': 'Enable',
'Enabled': 'Enabled',
'Disable': 'Disable',
'Disabled': 'Disabled',
'Version': 'Version',
'User': 'User',
'Application': 'Application',
@@ -128,6 +134,16 @@ export default {
'Other Device': 'Other Device',
'Are you sure you want to logout from this session?': 'Are you sure you want to logout from this session?',
'Unable to logout from this session': 'Unable to logout from this session',
'Regenerate Backup Codes': 'Regenerate Backup Codes',
'Please use two factor authentication app scan the below qrcode and input current passcode': 'Please use two factor authentication app scan the below qrcode and input current passcode',
'Please enter your current password when disable two factor authentication': 'Please enter your current password when disable two factor authentication',
'Please enter your current password when regenerate two factor authentication backup codes. If you regenerate backup codes, the old codes will be invalidated.': 'Please enter your current password when regenerate two factor authentication backup codes. If you regenerate backup codes, the old codes will be invalidated.',
'Please copy these backup codes to safe place, the below codes can only be shown once. If these codes were lost, you can regenerate backup codes at any time.': 'Please copy these backup codes to safe place, the below codes can only be shown once. If these codes were lost, you can regenerate backup codes at any time.',
'Backup codes copied': 'Backup codes copied',
'Two factor authentication has been disabled': 'Two factor authentication has been disabled',
'Unable to get current two factor authentication status': 'Unable to get current two factor authentication status',
'Unable to enable two factor authentication': 'Unable to enable two factor authentication',
'Unable to disable two factor authentication': 'Unable to disable two factor authentication',
'Log Out': 'Log Out',
'Are you sure you want to log out?': 'Are you sure you want to log out?',
'Unable to logout': 'Unable to logout',
+16
View File
@@ -46,6 +46,7 @@ export default {
'parameter': {
'username': '用户名',
'password': '密码',
'passcode': '验证码',
'email': '电子邮箱',
'nickname': '昵称',
'oldPassword': '当前密码',
@@ -66,6 +67,11 @@ export default {
'Update': '更新',
'Done': '完成',
'Continue': '继续',
'Status': '状态',
'Enable': '启用',
'Enabled': '启用',
'Disable': '禁用',
'Disabled': '禁用',
'Version': '版本',
'User': '用户',
'Application': '应用',
@@ -128,6 +134,16 @@ export default {
'Other Device': '其他设备',
'Are you sure you want to logout from this session?': '您确定是否要退出该会话?',
'Unable to logout from this session': '无法退出该会话',
'Regenerate Backup Codes': '重新生成备用码',
'Please use two factor authentication app scan the below qrcode and input current passcode': '请使用两步验证应用扫描下方的二维码并输入当前的验证码',
'Please enter your current password when disable two factor authentication': '禁用两步验证时需要输入您的当前密码',
'Please enter your current password when regenerate two factor authentication backup codes. If you regenerate backup codes, the old codes will be invalidated.': '重新生成两步验证备用码时需要输入您的当前密码。如果您重新生成备用码,之前的备用码将失效。',
'Please copy these backup codes to safe place, the below codes can only be shown once. If these codes were lost, you can regenerate backup codes at any time.': '请将备用码复制到安全的地方,下列备用码只会展示一次。如果这些备用码丢失,您可以随时重新生成备用码。',
'Backup codes copied': '备用码已复制',
'Two factor authentication has been disabled': '两步验证已经禁用',
'Unable to get current two factor authentication status': '无法获取当前两步验证状态',
'Unable to enable two factor authentication': '无法启用两步验证',
'Unable to disable two factor authentication': '无法禁用两步验证',
'Log Out': '退出登录',
'Are you sure you want to log out?': '您确定是否要退出登录?',
'Unable to logout': '无法退出登录',
+2
View File
@@ -4,6 +4,7 @@ import VueI18nFilter from 'vue-i18n-filter'
import Framework7 from 'framework7/framework7.esm.bundle.js';
import Framework7Vue from 'framework7-vue/framework7-vue.esm.bundle.js';
import VueMoment from 'vue-moment';
import VueClipboard from 'vue-clipboard2';
import moment from 'moment';
import 'moment/min/locales';
@@ -21,6 +22,7 @@ import App from './Mobile.vue';
Vue.use(VueI18n);
Vue.use(VueI18nFilter);
Vue.use(VueMoment, { moment });
Vue.use(VueClipboard);
Framework7.use(Framework7Vue);
const i18n = new VueI18n(getI18nOptions());
+6
View File
@@ -8,6 +8,7 @@ import SignUpPage from '../views/mobile/Signup.vue';
import SettingsPage from '../views/mobile/Settings.vue';
import AboutPage from "../views/mobile/About.vue";
import UserProfilePage from "../views/mobile/users/UserProfile.vue";
import TwoFactorAuthPage from "../views/mobile/users/TwoFactorAuth.vue";
import SessionListPage from "../views/mobile/users/SessionList.vue";
function checkLogin(to, from, resolve, reject) {
@@ -73,6 +74,11 @@ const routes = [
component: UserProfilePage,
beforeEnter: checkLogin
},
{
path: '/user/2fa',
component: TwoFactorAuthPage,
beforeEnter: checkLogin
},
{
path: '/user/sessions',
component: SessionListPage,
+6
View File
@@ -151,6 +151,12 @@
<span class="work-break-all">https://github.com/brockpetrie/vue-moment</span><br/>
<span class="work-break-all">License: https://github.com/brockpetrie/vue-moment/blob/master/LICENSE</span>
</p>
<p>
<strong>vue-clipboard2</strong><br/>
<span>Copyright (c) 2017 Inndy &lt;inndy \dot tw \at gmail \dot com&gt;</span><br/>
<span class="work-break-all">https://github.com/Inndy/vue-clipboard2</span><br/>
<span class="work-break-all">License: https://github.com/Inndy/vue-clipboard2/blob/master/LICENSE</span>
</p>
<p>
<strong>core-js</strong><br/>
<span>Copyright (c) 2014-2020 Denis Pushkarev</span><br/>
+1 -1
View File
@@ -4,7 +4,7 @@
<f7-block-title>{{ userNickName }}</f7-block-title>
<f7-list>
<f7-list-item :title="$t('User Profile')" link="/user/profile"></f7-list-item>
<f7-list-item :title="$t('Two-Factor Authentication')" link="/user/2fa/overview"></f7-list-item>
<f7-list-item :title="$t('Two-Factor Authentication')" link="/user/2fa"></f7-list-item>
<f7-list-item :title="$t('Device & Sessions')" link="/user/sessions"></f7-list-item>
<f7-list-button @click="logout">{{ $t('Log Out') }}</f7-list-button>
</f7-list>
+372
View File
@@ -0,0 +1,372 @@
<template>
<f7-page>
<f7-navbar :title="$t('Two-Factor Authentication')" :back-link="$t('Back')"></f7-navbar>
<f7-list v-if="status === true">
<f7-list-item :title="$t('Status')" :after="$t('Enabled')"></f7-list-item>
<f7-list-button @click="regenerateBackupCode(null)">{{ $t('Regenerate Backup Codes') }}</f7-list-button>
<f7-list-button @click="disable(null)">{{ $t('Disable') }}</f7-list-button>
</f7-list>
<f7-list v-else-if="status === false">
<f7-list-item :title="$t('Status')" :after="$t('Disabled')"></f7-list-item>
<f7-list-button @click="enable">{{ $t('Enable') }}</f7-list-button>
</f7-list>
<f7-sheet
style="height:auto; --f7-sheet-bg-color: #fff;"
backdrop
close-on-escape
:opened="showInputPasscodeSheetForEnable" @sheet:closed="showInputPasscodeSheetForEnable = false; currentPasscodeForEnable = ''"
>
<div class="sheet-modal-swipe-step">
<div class="display-flex padding justify-content-space-between align-items-center">
<div style="font-size: 18px"><b v-t="'Passcode'"></b></div>
</div>
<div class="padding-horizontal padding-bottom">
<p class="input-passcode-tips">{{ $t('Please use two factor authentication app scan the below qrcode and input current passcode') }}</p>
<div class="input-passcode-qrcode-container">
<img class="input-passcode-qrcode-img" :src="new2FAQRCode" />
</div>
<f7-list no-hairlines class="input-passcode-form">
<f7-list-input
type="number"
outline
clear-button
:placeholder="$t('Passcode')"
:value="currentPasscodeForEnable"
@input="currentPasscodeForEnable = $event.target.value"
></f7-list-input>
</f7-list>
<f7-button large fill :class="{ 'disabled': !currentPasscodeForEnable }" :text="$t('Continue')" @click="enableConfirm"></f7-button>
<div class="margin-top text-align-center">
<f7-link @click="showInputPasscodeSheetForEnable = false" :text="$t('Cancel')"></f7-link>
</div>
</div>
</div>
</f7-sheet>
<f7-sheet
style="height:auto; --f7-sheet-bg-color: #fff;"
backdrop
close-on-escape
:opened="showInputPasswordSheetForDisable" @sheet:closed="showInputPasswordSheetForDisable = false; currentPasswordForDisable = ''"
>
<div class="sheet-modal-swipe-step">
<div class="display-flex padding justify-content-space-between align-items-center">
<div style="font-size: 18px"><b v-t="'Current Password'"></b></div>
</div>
<div class="padding-horizontal padding-bottom">
<p class="input-password-tips">{{ $t('Please enter your current password when disable two factor authentication') }}</p>
<f7-list no-hairlines class="input-password-form">
<f7-list-input
type="password"
outline
clear-button
:placeholder="$t('Password')"
:value="currentPasswordForDisable"
@input="currentPasswordForDisable = $event.target.value"
></f7-list-input>
</f7-list>
<f7-button large fill :class="{ 'disabled': !currentPasswordForDisable }" :text="$t('Continue')" @click="disable(currentPasswordForDisable)"></f7-button>
<div class="margin-top text-align-center">
<f7-link @click="showInputPasswordSheetForDisable = false" :text="$t('Cancel')"></f7-link>
</div>
</div>
</div>
</f7-sheet>
<f7-sheet
style="height:auto; --f7-sheet-bg-color: #fff;"
backdrop
close-on-escape
:opened="showInputPasswordSheetForRegenerate" @sheet:closed="showInputPasswordSheetForRegenerate = false; currentPasswordForRegenerate= ''"
>
<div class="sheet-modal-swipe-step">
<div class="display-flex padding justify-content-space-between align-items-center">
<div style="font-size: 18px"><b v-t="'Current Password'"></b></div>
</div>
<div class="padding-horizontal padding-bottom">
<p class="input-password-tips">{{ $t('Please enter your current password when regenerate two factor authentication backup codes. If you regenerate backup codes, the old codes will be invalidated.') }}</p>
<f7-list no-hairlines class="input-password-form">
<f7-list-input
type="password"
outline
clear-button
:placeholder="$t('Password')"
:value="currentPasswordForRegenerate"
@input="currentPasswordForRegenerate = $event.target.value"
></f7-list-input>
</f7-list>
<f7-button large fill :class="{ 'disabled': !currentPasswordForRegenerate }" :text="$t('Continue')" @click="regenerateBackupCode(currentPasswordForRegenerate)"></f7-button>
<div class="margin-top text-align-center">
<f7-link @click="showInputPasswordSheetForRegenerate = false" :text="$t('Cancel')"></f7-link>
</div>
</div>
</div>
</f7-sheet>
<f7-sheet
style="height:auto; --f7-sheet-bg-color: #fff;"
backdrop
close-on-escape
:opened="showBackupCodeSheet" @sheet:closed="showBackupCodeSheet = false; currentBackupCode = ''"
>
<div class="sheet-modal-swipe-step">
<div class="display-flex padding justify-content-space-between align-items-center">
<div style="font-size: 18px"><b v-t="'Backup Code'"></b></div>
</div>
<div class="padding-horizontal padding-bottom">
<p class="input-password-tips">
<span>{{ $t('Please copy these backup codes to safe place, the below codes can only be shown once. If these codes were lost, you can regenerate backup codes at any time.') }}</span>
<f7-link icon-only icon-f7="doc_on_doc" icon-size="16px" class="icon-after-text"
v-clipboard:copy="currentBackupCode" v-clipboard:success="onBackupCodeCopied"></f7-link>
</p>
<textarea class="backup-code-textarea" rows="10" readonly="readonly" v-model="currentBackupCode"></textarea>
<div class="margin-top text-align-center">
<f7-link @click="showBackupCodeSheet = false" :text="$t('Close')"></f7-link>
</div>
</div>
</div>
</f7-sheet>
</f7-page>
</template>
<script>
export default {
data() {
return {
status: null,
new2FASecret: '',
new2FAQRCode: '',
currentPasscodeForEnable: '',
currentPasswordForDisable: '',
currentPasswordForRegenerate: '',
currentBackupCode: '',
showInputPasscodeSheetForEnable: false,
showInputPasswordSheetForDisable: false,
showInputPasswordSheetForRegenerate: false,
showBackupCodeSheet: false
};
},
created() {
const self = this;
const app = self.$f7;
const router = self.$f7router;
app.preloader.show();
self.$services.get2FAStatus().then(response => {
app.preloader.hide();
const data = response.data;
if (!data || !data.success || !data.result || typeof(data.result.enable) !== 'boolean') {
self.$alert('Unable to get current two factor authentication status', () => {
router.back();
});
return;
}
self.status = data.result.enable;
}).catch(error => {
app.preloader.hide();
if (error.response && error.response.data && error.response.data.errorMessage) {
self.$alert({ error: error.response.data }, () => {
router.back();
});
} else if (!error.processed) {
self.$alert('Unable to get current two factor authentication status', () => {
router.back();
});
}
});
},
methods: {
enable() {
const self = this;
const app = self.$f7;
self.new2FAQRCode = '';
self.new2FASecret = '';
app.preloader.show();
self.$services.enable2FA().then(response => {
app.preloader.hide();
const data = response.data;
if (!data || !data.success || !data.result || !data.result.qrcode || !data.result.secret) {
self.$alert('Unable to enable two factor authentication');
return;
}
self.new2FAQRCode = data.result.qrcode;
self.new2FASecret = data.result.secret;
self.showInputPasscodeSheetForEnable = true;
}).catch(error => {
app.preloader.hide();
if (error.response && error.response.data && error.response.data.errorMessage) {
self.$alert({error: error.response.data});
} else if (!error.processed) {
self.$alert('Unable to enable two factor authentication');
}
});
},
enableConfirm() {
const self = this;
const app = self.$f7;
self.$services.confirmEnable2FA({
secret: self.new2FASecret,
passcode: self.currentPasscodeForEnable
}).then(response => {
app.preloader.hide();
const data = response.data;
if (!data || !data.success || !data.result || !data.result.token) {
self.$alert('Unable to enable two factor authentication');
return;
}
self.new2FAQRCode = '';
self.new2FASecret = '';
self.status = true;
self.showInputPasscodeSheetForEnable = false;
self.$user.updateToken(data.result.token);
if (data.result.recoveryCodes && data.result.recoveryCodes.length) {
self.currentBackupCode = data.result.recoveryCodes.join('\n');
self.showBackupCodeSheet = true;
}
}).catch(error => {
app.preloader.hide();
if (error.response && error.response.data && error.response.data.errorMessage) {
self.$alert({error: error.response.data});
} else if (!error.processed) {
self.$alert('Unable to enable two factor authentication');
}
});
},
disable(password) {
const self = this;
const app = self.$f7;
if (!password) {
self.currentPasswordForDisable = '';
self.showInputPasswordSheetForDisable = true;
return;
}
app.preloader.show();
self.$services.disable2FA({
password: password
}).then(response => {
app.preloader.hide();
const data = response.data;
if (!data || !data.success || !data.result) {
self.$alert('Unable to disable two factor authentication');
return;
}
self.status = false;
self.showInputPasswordSheetForDisable = false;
self.$toast('Two factor authentication has been disabled');
}).catch(error => {
app.preloader.hide();
if (error.response && error.response.data && error.response.data.errorMessage) {
self.$alert({error: error.response.data});
} else if (!error.processed) {
self.$alert('Unable to disable two factor authentication');
}
});
},
regenerateBackupCode(password) {
const self = this;
const app = self.$f7;
if (!password) {
self.currentPasswordForRegenerate = '';
self.showInputPasswordSheetForRegenerate = true;
return;
}
app.preloader.show();
self.$services.regenerate2FARecoveryCode({
password: password
}).then(response => {
app.preloader.hide();
const data = response.data;
if (!data || !data.success || !data.result || !data.result.recoveryCodes || !data.result.recoveryCodes.length) {
self.$alert('Unable to regenerate two factor authentication backup codes');
return;
}
self.showInputPasswordSheetForRegenerate = false;
self.currentBackupCode = data.result.recoveryCodes.join('\n');
self.showBackupCodeSheet = true;
}).catch(error => {
app.preloader.hide();
if (error.response && error.response.data && error.response.data.errorMessage) {
self.$alert({error: error.response.data});
} else if (!error.processed) {
self.$alert('Unable to regenerate two factor authentication backup codes');
}
});
},
onBackupCodeCopied() {
this.$toast('Backup codes copied');
}
}
};
</script>
<style scoped>
.input-passcode-tips {
margin-top: 0;
}
.input-passcode-qrcode-container {
width: 100%;
text-align: center;
}
.input-passcode-qrcode-img {
width: 240px;
height: 240px;
}
.input-passcode-form {
margin-top: 0;
margin-bottom: 10px;
}
.input-password-tips {
margin-top: 0;
}
.input-password-tips .icon-after-text {
margin-left: 6px;
}
.input-password-form {
margin-top: 0;
margin-bottom: 10px;
}
.backup-code-textarea {
width: 100%;
}
</style>