adding otp support

This commit is contained in:
balzack 2024-08-12 15:03:08 +02:00
parent 5f13bc4957
commit 4d2b9b490f
4 changed files with 87 additions and 13 deletions

View File

@ -1,3 +1,26 @@
.mfa {
display: flex;
flex-direction: column;
align-items: center;
.mfaTitle {
font-size: 1.5rem;
}
.mfaPin {
padding: 32px;
}
.mfaDescription {
font-size: 1.1rem;
}
.mfaControl {
display: flex;
gap: 8px;
}
}
.split {
display: flex;
height: 100%;

View File

@ -10,6 +10,7 @@ import {
Modal,
PasswordInput,
TextInput,
PinInput,
} from '@mantine/core'
import { useDisclosure } from '@mantine/hooks'
import left from '../images/login.png'
@ -26,13 +27,20 @@ export function Access() {
const [alertOpened, { open: alertOpen, close: alertClose }] =
useDisclosure(false)
const [urlOpened, { open: urlOpen, close: urlClose }] = useDisclosure(false)
const [otpOpened, { open: otpOpen, close: otpClose }] = useDisclosure(false)
const login = async () => {
try {
await actions.accountLogin()
} catch (err) {
console.log(err)
alertOpen()
if (!state.loading) {
actions.setLoading(true)
otpClose()
try {
await new Promise((r) => setTimeout(r, 2000))
await actions.accountLogin()
} catch (err) {
console.log(err)
alertOpen()
}
actions.setLoading(false)
}
}
@ -104,6 +112,7 @@ export function Access() {
variant="filled"
className={classes.submit}
onClick={login}
loading={state.loading}
disabled={!state.username || !state.password}
>
{state.strings.login}
@ -118,13 +127,13 @@ export function Access() {
<Button
size="compact-sm"
variant="subtle"
onClick={() => actions.setMode('access')}
onClick={() => actions.setMode('reset')}
>
{state.strings.forgotPassword}
</Button>
</>
)}
{state.mode === 'access' && (
{state.mode === 'reset' && (
<>
<Title order={3}>{state.strings.accessAccount}</Title>
<Space h="md" />
@ -138,9 +147,10 @@ export function Access() {
<TextInput
className={classes.input}
size="md"
value={state.token}
leftSectionPointerEvents="none"
leftSection={<IconKey />}
placeholder={state.strings.accessCode}
placeholder={state.strings.resetCode}
onChange={(event) =>
actions.setToken(event.currentTarget.value)
}
@ -173,10 +183,11 @@ export function Access() {
<TextInput
className={classes.input}
size="md"
value={state.token}
disabled={!state.availableSet}
leftSectionPointerEvents="none"
leftSection={<IconKey />}
placeholder={state.strings.accessCode}
placeholder={state.strings.resetCode}
onChange={(event) =>
actions.setToken(event.currentTarget.value)
}
@ -292,6 +303,30 @@ export function Access() {
>
{state.strings.tryAgain}
</Modal>
<Modal
opened={otpOpened}
onClose={otpClose}
withCloseButton={false}
centered
>
<div className={classes.mfa}>
<div className={classes.mfaTitle}>{state.strings.mfaTitle}</div>
<div className={classes.mfaDescription}>{state.strings.mfaEnter}</div>
<PinInput
length={6}
className={classes.mfaPin}
onChange={(event) => actions.setCode(event.currentTarget.value)}
/>
<div className={classes.mfaControl}>
<Button variant="outline" onClick={otpClose}>
{state.strings.cancel}
</Button>
<Button variant="filled" onClick={login}>
{state.strings.login}
</Button>
</div>
</div>
</Modal>
</div>
)
}

View File

@ -10,14 +10,16 @@ export function useAccess() {
const [state, setState] = useState({
display: null,
strings: settings.state.strings,
mode: 'login',
mode: '',
username: '',
password: '',
confirm: '',
token: '',
code: '',
theme: '',
language: '',
node: '',
loading: false,
secure: false,
host: '',
available: 0,
@ -32,6 +34,16 @@ export function useAccess() {
}
useEffect(() => {
const params = new URLSearchParams(location)
const search = params.get('search')
if (search && search.startsWith('?create=')) {
updateState({ mode: 'create', token: search.substring(8) })
} else if (search && search.startsWith('?reset=')) {
updateState({ mode: 'reset', token: search.substring(7) })
} else {
updateState({ mode: 'login' })
}
const { protocol, host } = location
setUrl(`${protocol}//${host}`)
}, [])
@ -92,6 +104,9 @@ export function useAccess() {
setToken: (token: string) => {
updateState({ token })
},
setCode: (code: string) => {
updateState({ code })
},
setNode: (node: string) => {
setUrl(node)
},
@ -101,6 +116,9 @@ export function useAccess() {
setTheme: (theme: string) => {
settings.actions.setTheme(theme)
},
setLoading: (loading: boolean) => {
updateState({ loading })
},
accountLogin: async () => {
const { username, password, host, secure } = state
await app.actions.accountLogin(username, password, host, secure)

View File

@ -32,8 +32,6 @@ export function useAppContext() {
node: string,
secure: boolean
) => {
console.log('LOGIN:', username, password, node, secure)
const params = {
topicBatch: 16,
tagBatch: 16,
@ -50,7 +48,7 @@ export function useAppContext() {
password,
node,
secure,
null,
code,
params
)
updateState({ session: login })