mirror of
https://github.com/balzack/databag.git
synced 2025-04-23 18:15:19 +00:00
adding mfa modal
This commit is contained in:
parent
65cbf70528
commit
4161f60648
@ -1,3 +1,27 @@
|
||||
.mfa {
|
||||
.secret {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
|
||||
.secretImage {
|
||||
width: 192px;
|
||||
height: 192px;
|
||||
}
|
||||
|
||||
.secretText {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.copyIcon {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.change {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
Select,
|
||||
Switch,
|
||||
Text,
|
||||
PinInput,
|
||||
Image,
|
||||
Button,
|
||||
UnstyledButton,
|
||||
@ -33,6 +34,8 @@ import {
|
||||
IconMapPin,
|
||||
IconLogout,
|
||||
IconLogin,
|
||||
IconCopy,
|
||||
IconCheck,
|
||||
} from '@tabler/icons-react'
|
||||
import { modals } from '@mantine/modals'
|
||||
import { useDisclosure } from '@mantine/hooks'
|
||||
@ -60,6 +63,7 @@ export function Settings({ showLogout }: { showLogout: boolean }) {
|
||||
const [savingNotifications, setSavingNotifications] = useState(false)
|
||||
const [savingSeal, setSavingSeal] = useState(false)
|
||||
const [savingMfa, setSavingMfa] = useState(false)
|
||||
const [addingMfa, setAddingMfa] = useState(false)
|
||||
|
||||
const logout = () =>
|
||||
modals.openConfirmModal({
|
||||
@ -114,7 +118,7 @@ export function Settings({ showLogout }: { showLogout: boolean }) {
|
||||
}
|
||||
}
|
||||
|
||||
const setSeal = async (checked: boolean) => {
|
||||
const setSeal = async () => {
|
||||
if (!savingSeal) {
|
||||
sealOpen();
|
||||
setSavingSeal(true);
|
||||
@ -122,10 +126,26 @@ export function Settings({ showLogout }: { showLogout: boolean }) {
|
||||
}
|
||||
}
|
||||
|
||||
const setMfa = async () => {
|
||||
const setMfa = async (checked: boolean) => {
|
||||
if (!addingMfa) {
|
||||
setAddingMfa(true);
|
||||
try {
|
||||
await actions.enableMFA();
|
||||
mfaOpen();
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
showError()
|
||||
}
|
||||
setAddingMfa(false);
|
||||
}
|
||||
}
|
||||
|
||||
const confirmMfa = async () => {
|
||||
if (!savingMfa) {
|
||||
mfaOpen();
|
||||
setSavingMfa(true);
|
||||
|
||||
mfaClose();
|
||||
|
||||
setSavingMfa(false);
|
||||
}
|
||||
}
|
||||
@ -615,7 +635,35 @@ export function Settings({ showLogout }: { showLogout: boolean }) {
|
||||
overlayProps={{ backgroundOpacity: 0.55, blur: 3 }}
|
||||
centered
|
||||
>
|
||||
<div className={classes.mfa} />
|
||||
<div className={classes.mfa}>
|
||||
<div className={classes.secret}>
|
||||
<Text>{state.strings.mfaSteps}</Text>
|
||||
<Image radius="md" className={classes.secretImage} src={state.secretImage} />
|
||||
<div className={classes.secretText}>
|
||||
<Text>{state.secretText}</Text>
|
||||
{ state.secretCopied && (
|
||||
<IconCheck />
|
||||
)}
|
||||
{ !state.secretCopied && (
|
||||
<IconCopy className={classes.copyIcon} onClick={actions.copySecret} />
|
||||
)}
|
||||
</div>
|
||||
<PinInput
|
||||
value={state.code}
|
||||
length={6}
|
||||
className={classes.mfaPin}
|
||||
onChange={(event) => actions.setCode(event)}
|
||||
/>
|
||||
</div>
|
||||
<div className={classes.control}>
|
||||
<Button variant="default" onClick={mfaClose}>
|
||||
{state.strings.cancel}
|
||||
</Button>
|
||||
<Button variant="filled" onClick={confirmMfa} disabled={state.code.length != 6} loading={savingMfa}>
|
||||
{state.strings.save}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
<Modal
|
||||
title={state.strings.sealedTopics}
|
||||
|
@ -48,7 +48,10 @@ export function useSettings() {
|
||||
clip: { w: 0, h: 0, x: 0, y: 0 },
|
||||
crop: { x: 0, y: 0 },
|
||||
zoom: 1,
|
||||
editImage: undefined,
|
||||
secretText: '',
|
||||
secretImage: '',
|
||||
code: '',
|
||||
editImage: '',
|
||||
})
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@ -149,7 +152,8 @@ export function useSettings() {
|
||||
},
|
||||
enableMFA: async () => {
|
||||
const { settings } = getSession()
|
||||
return await settings.enableMFA()
|
||||
const { secretImage, secretText } = await settings.enableMFA()
|
||||
updateState({ secretImage, secretText });
|
||||
},
|
||||
disableMFA: async () => {
|
||||
const { settings } = getSession()
|
||||
@ -159,6 +163,16 @@ export function useSettings() {
|
||||
const { settings } = getSession()
|
||||
await settings.confirmMFA(code)
|
||||
},
|
||||
setCode: (code: string) => {
|
||||
updateState({ code });
|
||||
},
|
||||
copySecret: () => {
|
||||
navigator.clipboard.writeText(state.secretText);
|
||||
updateState({ secretCopied: true });
|
||||
setTimeout(() => {
|
||||
updateState({ secretCopied: false });
|
||||
}, 1000);
|
||||
},
|
||||
setSeal: async (password: string) => {
|
||||
const { settings } = getSession()
|
||||
await settings.setSeal(password)
|
||||
@ -279,8 +293,8 @@ export function useSettings() {
|
||||
img,
|
||||
state.clip.x,
|
||||
state.clip.y,
|
||||
state.clip.width,
|
||||
state.clip.height,
|
||||
state.clip.w,
|
||||
state.clip.h,
|
||||
0,
|
||||
0,
|
||||
IMAGE_DIM,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { checkResponse, fetchWithTimeout } from './fetchUtil';
|
||||
|
||||
export async function addAccountMFAuth(node: string, secure: boolean, token: string): Promise<{ text: string, image: string }> {
|
||||
const endpoint = `http${secure ? 's' : ''}://${node}/account/mfauth=${token}`;
|
||||
const endpoint = `http${secure ? 's' : ''}://${node}/account/mfauth?agent=${token}`;
|
||||
const auth = await fetchWithTimeout(endpoint, { method: 'POST' })
|
||||
checkResponse(auth.status);
|
||||
return await auth.json();
|
||||
|
@ -145,8 +145,8 @@ export class SettingsModule implements Settings {
|
||||
|
||||
public async enableMFA(): Promise<{ secretImage: string, secretText: string }> {
|
||||
const { node, secure, token } = this;
|
||||
const { image, text } = await addAccountMFAuth(node, secure, token);
|
||||
return { secretImage: image, secretText: text };
|
||||
const { secretImage, secretText } = await addAccountMFAuth(node, secure, token);
|
||||
return { secretImage, secretText };
|
||||
}
|
||||
|
||||
public async disableMFA(): Promise<void> {
|
||||
|
Loading…
x
Reference in New Issue
Block a user