nginx-ui/app/src/components/TwoFA/use2FAModal.ts

101 lines
2.8 KiB
TypeScript

import { createVNode, render } from 'vue'
import { Modal, message } from 'ant-design-vue'
import { useCookies } from '@vueuse/integrations/useCookies'
import Authorization from '@/components/TwoFA/Authorization.vue'
import twoFA from '@/api/2fa'
import { useUserStore } from '@/pinia'
const use2FAModal = () => {
const refOTPAuthorization = ref<typeof Authorization>()
const randomId = Math.random().toString(36).substring(2, 8)
const { secureSessionId } = storeToRefs(useUserStore())
const injectStyles = () => {
const style = document.createElement('style')
style.innerHTML = `
.${randomId} .ant-modal-title {
font-size: 1.125rem;
}
`
document.head.appendChild(style)
}
const open = async (): Promise<string> => {
const twoFAStatus = await twoFA.status()
const { status: secureSessionStatus } = await twoFA.secure_session_status()
return new Promise((resolve, reject) => {
if (!twoFAStatus.enabled) {
resolve('')
return
}
const cookies = useCookies(['nginx-ui-2fa'])
const ssid = cookies.get('secure_session_id')
if (ssid && secureSessionStatus) {
resolve(ssid)
secureSessionId.value = ssid
return
}
injectStyles()
let container: HTMLDivElement | null = document.createElement('div')
document.body.appendChild(container)
const close = () => {
render(null, container!)
document.body.removeChild(container!)
container = null
}
const setSessionId = (sessionId: string) => {
cookies.set('secure_session_id', sessionId, { maxAge: 60 * 3 })
close()
secureSessionId.value = sessionId
resolve(sessionId)
}
const verifyOTP = (passcode: string, recovery: string) => {
twoFA.start_secure_session_by_otp(passcode, recovery).then(async r => {
setSessionId(r.session_id)
}).catch(async () => {
refOTPAuthorization.value?.clearInput()
await message.error($gettext('Invalid passcode or recovery code'))
})
}
const vnode = createVNode(Modal, {
open: true,
title: $gettext('Two-factor authentication required'),
centered: true,
maskClosable: false,
class: randomId,
footer: false,
onCancel: () => {
close()
// eslint-disable-next-line prefer-promise-reject-errors
reject()
},
}, {
default: () => h(
Authorization,
{
ref: refOTPAuthorization,
twoFAStatus,
class: 'mt-3',
onSubmitOTP: verifyOTP,
onSubmitSecureSessionID: setSessionId,
},
),
})
render(vnode, container!)
})
}
return { open }
}
export default use2FAModal