chore: implement 2fa auth (#2968)

* chore: implement 2fa auth

from #2786

* chore: format code

* chore: replace two factor token input with qr-code

* chore: requesting confirmation of setting/removing two-factor authentication

otpauth library was taken from cdnjs

* chore: revert changes in `ClipboardManager`

don't need it.

* chore: removing twoFactor prop in settings page

* chore: remove `twoFactorQr` object in `mounted` function
This commit is contained in:
Shishkevich D.
2025-05-08 21:20:58 +07:00
committed by GitHub
parent d39ccf4b8f
commit fe3b1c9b52
31 changed files with 452 additions and 302 deletions

View File

@@ -122,10 +122,13 @@
</a-layout>
</a-layout>
{{template "js" .}}
<script src="{{ .base_path }}assets/qrcode/qrious2.min.js?{{ .cur_ver }}"></script>
<script src="{{ .base_path }}assets/otpauth/otpauth.umd.min.js?{{ .cur_ver }}"></script>
<script src="{{ .base_path }}assets/js/model/setting.js?{{ .cur_ver }}"></script>
{{template "component/aSidebar" .}}
{{template "component/aThemeSwitch" .}}
{{template "component/aSettingListItem" .}}
{{template "modals/twoFactorModal"}}
<script>
const app = new Vue({
delimiters: ['[[', ']]'],
@@ -133,7 +136,6 @@
data: {
themeSwitcher,
spinning: false,
changeSecret: false,
oldAllSetting: new AllSetting(),
allSetting: new AllSetting(),
saveBtnDisable: true,
@@ -258,7 +260,6 @@
app.changeRemarkSample();
this.saveBtnDisable = true;
}
await this.fetchUserSecret();
},
async updateAllSetting() {
this.loading(true);
@@ -302,38 +303,34 @@
window.location.replace(url);
}
},
async fetchUserSecret() {
this.loading(true);
const userMessage = await HttpUtil.post("/panel/setting/getUserSecret", this.user);
if (userMessage.success) {
this.user = userMessage.obj;
}
this.loading(false);
},
async updateSecret() {
this.loading(true);
const msg = await HttpUtil.post("/panel/setting/updateUserSecret", this.user);
if (msg && msg.obj) {
this.user = msg.obj;
}
this.loading(false);
await this.updateAllSetting();
},
async getNewSecret() {
if (!this.changeSecret) {
this.changeSecret = true;
this.user.loginSecret = '';
const newSecret = RandomUtil.randomSeq(64);
await PromiseUtil.sleep(1000);
this.user.loginSecret = newSecret;
this.changeSecret = false;
}
},
async toggleToken(value) {
if (value) {
await this.getNewSecret();
toggleTwoFactor(newValue) {
if (newValue) {
const newTwoFactorToken = RandomUtil.randomBase32String()
twoFactorModal.show({
title: '{{ i18n "pages.settings.security.twoFactorModalSetTitle" }}',
token: newTwoFactorToken,
type: 'set',
confirm: (success) => {
if (success) {
this.allSetting.twoFactorToken = newTwoFactorToken
}
this.allSetting.twoFactorEnable = success
}
})
} else {
this.user.loginSecret = "";
twoFactorModal.show({
title: '{{ i18n "pages.settings.security.twoFactorModalDeleteTitle" }}',
token: this.allSetting.twoFactorToken,
type: 'remove',
confirm: (success) => {
if (success) {
this.allSetting.twoFactorEnable = false
this.allSetting.twoFactorToken = ""
}
}
})
}
},
addNoise() {
@@ -526,6 +523,7 @@
},
async mounted() {
await this.getAllSetting();
while (true) {
await PromiseUtil.sleep(1000);
this.saveBtnDisable = this.oldAllSetting.equals(this.allSetting);