mirror of
https://github.com/Myzel394/kleckrelay-website.git
synced 2025-06-19 15:55:26 +02:00
added ability to login from different devices
This commit is contained in:
parent
ccdaf12ee3
commit
8ae24aec8e
@ -35,6 +35,7 @@
|
|||||||
"title": "You got mail!",
|
"title": "You got mail!",
|
||||||
"description": "We sent you a code to your email. Enter it below to login",
|
"description": "We sent you a code to your email. Enter it below to login",
|
||||||
"continueAction": "Log in",
|
"continueAction": "Log in",
|
||||||
|
"allowLoginFromDifferentDevices": "Allow login from different devices",
|
||||||
"form": {
|
"form": {
|
||||||
"code": {
|
"code": {
|
||||||
"label": "Verification Code",
|
"label": "Verification Code",
|
||||||
@ -43,6 +44,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"confirmFromDifferentDevice": {
|
||||||
|
"title": "Login failed",
|
||||||
|
"description": "You could not be logged in. This could either be because you are not allowed to login from different devices or the verification code is invalid or expired."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -15,7 +15,7 @@ export default async function changeAllowEmailLoginFromDifferentDevices({
|
|||||||
const {data} = await client.patch(
|
const {data} = await client.patch(
|
||||||
`${
|
`${
|
||||||
import.meta.env.VITE_SERVER_BASE_URL
|
import.meta.env.VITE_SERVER_BASE_URL
|
||||||
}/auth/login/email-token/allow-email-login-from-different-devices`,
|
}/auth/login/email-token/allow-login-from-different-devices`,
|
||||||
{
|
{
|
||||||
email,
|
email,
|
||||||
sameRequestToken,
|
sameRequestToken,
|
||||||
|
@ -5,7 +5,7 @@ import parseUser from "~/apis/helpers/parse-user"
|
|||||||
export interface VerifyLoginWithEmailData {
|
export interface VerifyLoginWithEmailData {
|
||||||
email: string
|
email: string
|
||||||
token: string
|
token: string
|
||||||
sameRequestToken: string
|
sameRequestToken?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function verifyLoginWithEmail({
|
export default async function verifyLoginWithEmail({
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import * as yup from "yup"
|
import * as yup from "yup"
|
||||||
import {AxiosError} from "axios"
|
import {AxiosError} from "axios"
|
||||||
import {ReactElement} from "react"
|
import {ReactElement, useState} from "react"
|
||||||
import {useFormik} from "formik"
|
import {useFormik} from "formik"
|
||||||
import {FaHashtag} from "react-icons/fa"
|
import {FaHashtag} from "react-icons/fa"
|
||||||
import {MdChevronRight, MdMail} from "react-icons/md"
|
import {MdChevronRight, MdMail} from "react-icons/md"
|
||||||
@ -8,13 +8,27 @@ import {useLoaderData} from "react-router-dom"
|
|||||||
import {useTranslation} from "react-i18next"
|
import {useTranslation} from "react-i18next"
|
||||||
|
|
||||||
import {useMutation} from "@tanstack/react-query"
|
import {useMutation} from "@tanstack/react-query"
|
||||||
import {Box, Grid, InputAdornment, TextField, Typography} from "@mui/material"
|
import {
|
||||||
|
Box,
|
||||||
|
FormControlLabel,
|
||||||
|
Grid,
|
||||||
|
InputAdornment,
|
||||||
|
Switch,
|
||||||
|
TextField,
|
||||||
|
Typography,
|
||||||
|
} from "@mui/material"
|
||||||
import {LoadingButton} from "@mui/lab"
|
import {LoadingButton} from "@mui/lab"
|
||||||
|
|
||||||
import {AuthenticationDetails, ServerSettings, ServerUser} from "~/server-types"
|
import {
|
||||||
|
AuthenticationDetails,
|
||||||
|
ServerSettings,
|
||||||
|
ServerUser,
|
||||||
|
SimpleDetailResponse,
|
||||||
|
} from "~/server-types"
|
||||||
import {VerifyLoginWithEmailData, verifyLoginWithEmail} from "~/apis"
|
import {VerifyLoginWithEmailData, verifyLoginWithEmail} from "~/apis"
|
||||||
import {MultiStepFormElement} from "~/components"
|
import {MultiStepFormElement} from "~/components"
|
||||||
import {parseFastAPIError} from "~/utils"
|
import {parseFastAPIError} from "~/utils"
|
||||||
|
import changeAllowEmailLoginFromDifferentDevices from "~/apis/change-allow-email-login-from-different-devices"
|
||||||
|
|
||||||
import ResendMailButton from "./ResendMailButton"
|
import ResendMailButton from "./ResendMailButton"
|
||||||
|
|
||||||
@ -84,6 +98,25 @@ export default function ConfirmCodeForm({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
const [allowLoginFromDifferentDevices, setAllowLoginFromDifferentDevices] =
|
||||||
|
useState<boolean>(false)
|
||||||
|
const {mutateAsync: changeAllowLoginFromDifferentDevice, isLoading} = useMutation<
|
||||||
|
SimpleDetailResponse,
|
||||||
|
AxiosError,
|
||||||
|
boolean
|
||||||
|
>(
|
||||||
|
allow =>
|
||||||
|
changeAllowEmailLoginFromDifferentDevices({
|
||||||
|
allow,
|
||||||
|
email,
|
||||||
|
sameRequestToken,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
onSuccess: (_, allow) => {
|
||||||
|
setAllowLoginFromDifferentDevices(allow)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MultiStepFormElement>
|
<MultiStepFormElement>
|
||||||
@ -110,6 +143,28 @@ export default function ConfirmCodeForm({
|
|||||||
{t("routes.LoginRoute.forms.confirmCode.description")}
|
{t("routes.LoginRoute.forms.confirmCode.description")}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Grid item>
|
||||||
|
<Grid container spacing={2} direction="column">
|
||||||
|
<Grid item>
|
||||||
|
<FormControlLabel
|
||||||
|
disabled={isLoading}
|
||||||
|
control={
|
||||||
|
<Switch
|
||||||
|
disabled={isLoading}
|
||||||
|
checked={allowLoginFromDifferentDevices}
|
||||||
|
onChange={() =>
|
||||||
|
changeAllowLoginFromDifferentDevice(
|
||||||
|
!allowLoginFromDifferentDevices,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
labelPlacement="end"
|
||||||
|
label={t(
|
||||||
|
"routes.LoginRoute.forms.confirmCode.allowLoginFromDifferentDevices",
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
<Grid item>
|
<Grid item>
|
||||||
<TextField
|
<TextField
|
||||||
key="code"
|
key="code"
|
||||||
@ -131,6 +186,8 @@ export default function ConfirmCodeForm({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
<Grid item>
|
<Grid item>
|
||||||
<Grid width="100%" container display="flex" justifyContent="space-between">
|
<Grid width="100%" container display="flex" justifyContent="space-between">
|
||||||
<Grid item>
|
<Grid item>
|
||||||
|
71
src/route-widgets/LoginRoute/ConfirmFromDifferentDevice.tsx
Normal file
71
src/route-widgets/LoginRoute/ConfirmFromDifferentDevice.tsx
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import {ReactElement} from "react"
|
||||||
|
import {AxiosError} from "axios"
|
||||||
|
import {useMount} from "react-use"
|
||||||
|
import {useTranslation} from "react-i18next"
|
||||||
|
|
||||||
|
import {useMutation} from "@tanstack/react-query"
|
||||||
|
import {Box, Grid, Paper, Typography} from "@mui/material"
|
||||||
|
|
||||||
|
import {AuthenticationDetails, ServerUser} from "~/server-types"
|
||||||
|
import {verifyLoginWithEmail} from "~/apis"
|
||||||
|
import LoadingData from "~/components/LoadingData"
|
||||||
|
|
||||||
|
export interface ConfirmFromDifferentDeviceProps {
|
||||||
|
email: string
|
||||||
|
token: string
|
||||||
|
|
||||||
|
onConfirm: (user: ServerUser) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ConfirmFromDifferentDevice({
|
||||||
|
email,
|
||||||
|
token,
|
||||||
|
onConfirm,
|
||||||
|
}: ConfirmFromDifferentDeviceProps): ReactElement {
|
||||||
|
const {t} = useTranslation()
|
||||||
|
const {mutate, isLoading, isError} = useMutation<AuthenticationDetails, AxiosError, void>(
|
||||||
|
() =>
|
||||||
|
verifyLoginWithEmail({
|
||||||
|
email,
|
||||||
|
token,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
onSuccess: ({user}) => onConfirm(user),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
useMount(mutate)
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<Paper>
|
||||||
|
<LoadingData />
|
||||||
|
</Paper>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isError) {
|
||||||
|
return (
|
||||||
|
<Paper>
|
||||||
|
<Box padding={4}>
|
||||||
|
<Grid container spacing={2} direction="column" alignItems="center">
|
||||||
|
<Grid item>
|
||||||
|
<Typography variant="h6" component="h1">
|
||||||
|
{t("routes.LoginRoute.forms.confirmFromDifferentDevice.title")}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item>
|
||||||
|
<Typography variant="body1">
|
||||||
|
{t(
|
||||||
|
"routes.LoginRoute.forms.confirmFromDifferentDevice.description",
|
||||||
|
)}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
</Paper>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return <></>
|
||||||
|
}
|
@ -3,12 +3,15 @@ import {useNavigate} from "react-router-dom"
|
|||||||
import {useUpdateEffect} from "react-use"
|
import {useUpdateEffect} from "react-use"
|
||||||
|
|
||||||
import {MultiStepForm} from "~/components"
|
import {MultiStepForm} from "~/components"
|
||||||
|
import {useQueryParams} from "~/hooks"
|
||||||
import AuthContext from "~/AuthContext/AuthContext"
|
import AuthContext from "~/AuthContext/AuthContext"
|
||||||
import ConfirmCodeForm from "~/route-widgets/LoginRoute/ConfirmCodeForm/ConfirmCodeForm"
|
import ConfirmCodeForm from "~/route-widgets/LoginRoute/ConfirmCodeForm/ConfirmCodeForm"
|
||||||
|
import ConfirmFromDifferentDevice from "~/route-widgets/LoginRoute/ConfirmFromDifferentDevice"
|
||||||
import EmailForm from "~/route-widgets/LoginRoute/EmailForm"
|
import EmailForm from "~/route-widgets/LoginRoute/EmailForm"
|
||||||
|
|
||||||
export default function LoginRoute(): ReactElement {
|
export default function LoginRoute(): ReactElement {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
const {token, email: queryEmail} = useQueryParams<{token: string; email: string}>()
|
||||||
const {login, user} = useContext(AuthContext)
|
const {login, user} = useContext(AuthContext)
|
||||||
|
|
||||||
const [email, setEmail] = useState<string>("")
|
const [email, setEmail] = useState<string>("")
|
||||||
@ -26,6 +29,10 @@ export default function LoginRoute(): ReactElement {
|
|||||||
}
|
}
|
||||||
}, [user?.encryptedPassword])
|
}, [user?.encryptedPassword])
|
||||||
|
|
||||||
|
if (token && queryEmail) {
|
||||||
|
return <ConfirmFromDifferentDevice email={queryEmail} token={token} onConfirm={login} />
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MultiStepForm
|
<MultiStepForm
|
||||||
steps={[
|
steps={[
|
||||||
|
Loading…
x
Reference in New Issue
Block a user