3x-ui/web/html/modals/two_factor_modal.html
Shishkevich D. fe3b1c9b52
Some checks failed
Build and Release 3X-UI / build (386) (push) Has been cancelled
Build and Release 3X-UI / build (amd64) (push) Has been cancelled
Build and Release 3X-UI / build (arm64) (push) Has been cancelled
Build and Release 3X-UI / build (armv5) (push) Has been cancelled
Build and Release 3X-UI / build (armv6) (push) Has been cancelled
Build and Release 3X-UI / build (armv7) (push) Has been cancelled
Build and Release 3X-UI / build (s390x) (push) Has been cancelled
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
2025-05-08 16:20:58 +02:00

118 lines
No EOL
4.3 KiB
HTML

{{define "modals/twoFactorModal"}}
<a-modal id="two-factor-modal" v-model="twoFactorModal.visible" :title="twoFactorModal.title" :closable="true"
:class="themeSwitcher.currentTheme">
<template v-if="twoFactorModal.type === 'set'">
<p>{{ i18n "pages.settings.security.twoFactorModalSteps" }}</p>
<a-divider></a-divider>
<p>{{ i18n "pages.settings.security.twoFactorModalFirstStep" }}</p>
<div :style="{ display: 'flex', alignItems: 'center', flexDirection: 'column', gap: '12px' }">
<div
:style="{ border: '1px solid', borderRadius: '1rem', borderColor: themeSwitcher.isDarkTheme ? 'var(--dark-color-surface-300)' : '#d9d9d9', padding: 0 }">
<img :src="twoFactorModal.qrImage"
:style="{ filter: themeSwitcher.isDarkTheme ? 'invert(1)' : 'none'}"
:alt="twoFactorModal.token">
</div>
<span :style="{ fontSize: '12px', fontFamily: 'monospace' }">[[ twoFactorModal.token ]]</span>
</div>
<a-divider></a-divider>
<p>{{ i18n "pages.settings.security.twoFactorModalSecondStep" }}</p>
<a-input v-model.trim="twoFactorModal.enteredCode" :style="{ width: '100%' }"></a-input>
</template>
<template v-if="twoFactorModal.type === 'remove'">
<p>{{ i18n "pages.settings.security.twoFactorModalRemoveStep" }}</p>
<a-input v-model.trim="twoFactorModal.enteredCode" :style="{ width: '100%' }"></a-input>
</template>
<template slot="footer">
<a-button @click="twoFactorModal.cancel">
<span>{{ i18n "cancel" }}</span>
</a-button>
<a-button type="primary" :disabled="twoFactorModal.enteredCode.length < 6" @click="twoFactorModal.ok">
<span>{{ i18n "confirm" }}</span>
</a-button>
</template>
</a-modal>
<script>
const twoFactorModal = {
title: '',
fileName: '',
token: '',
enteredCode: '',
visible: false,
type: 'set',
confirm: null,
totpObject: null,
qrImage: "",
ok() {
if (twoFactorModal.totpObject.generate() === twoFactorModal.enteredCode) {
ObjectUtil.execute(twoFactorModal.confirm, true)
twoFactorModal.close()
switch (twoFactorModal.type) {
case 'set':
Vue.prototype.$message['success']('{{ i18n "pages.settings.security.twoFactorModalSetSuccess" }}')
break;
case 'remove':
Vue.prototype.$message['success']('{{ i18n "pages.settings.security.twoFactorModalDeleteSuccess" }}')
break;
default:
break;
}
} else {
Vue.prototype.$message['error']('{{ i18n "pages.settings.security.twoFactorModalError" }}')
}
},
cancel() {
ObjectUtil.execute(twoFactorModal.confirm, false)
twoFactorModal.close()
},
show: function ({
title = '',
token = '',
type = 'set',
confirm = (success) => { }
}) {
this.title = title;
this.token = token;
this.visible = true;
this.confirm = confirm;
this.type = type;
this.totpObject = new OTPAuth.TOTP({
issuer: "3x-ui",
label: "Administrator",
algorithm: "SHA1",
digits: 6,
period: 30,
secret: twoFactorModal.token,
});
if (type === 'set') {
this.qrImage = new QRious({
size: 150,
value: twoFactorModal.totpObject.toString(),
background: 'white',
backgroundAlpha: 0,
foreground: 'black',
padding: 12,
level: 'L'
}).toDataURL()
}
},
close: function () {
twoFactorModal.enteredCode = "";
twoFactorModal.visible = false;
},
};
const twoFactorModalApp = new Vue({
delimiters: ['[[', ']]'],
el: '#two-factor-modal',
data: {
twoFactorModal: twoFactorModal,
},
});
</script>
{{end}}