chore: use monorepo organization
This commit is contained in:
parent
deaf7d6ecc
commit
a687827f7e
1099 changed files with 8184 additions and 11535 deletions
227
packages/admin-ui/src/pages/Authentication/LoginState.jsx
Normal file
227
packages/admin-ui/src/pages/Authentication/LoginState.jsx
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
import { useMutation, useLazyQuery, gql } from '@apollo/client'
|
||||
import { startAssertion } from '@simplewebauthn/browser'
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import React, { useContext } from 'react'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import { Label3, P } from 'src/components/typography'
|
||||
import * as Yup from 'yup'
|
||||
|
||||
import AppContext from 'src/AppContext'
|
||||
import { Button } from 'src/components/buttons'
|
||||
import { Checkbox, SecretInput, TextInput } from 'src/components/inputs/formik'
|
||||
|
||||
const LOGIN = gql`
|
||||
mutation login($username: String!, $password: String!) {
|
||||
login(username: $username, password: $password)
|
||||
}
|
||||
`
|
||||
|
||||
const GENERATE_ASSERTION = gql`
|
||||
query generateAssertionOptions($domain: String!) {
|
||||
generateAssertionOptions(domain: $domain)
|
||||
}
|
||||
`
|
||||
|
||||
const VALIDATE_ASSERTION = gql`
|
||||
mutation validateAssertion(
|
||||
$assertionResponse: JSONObject!
|
||||
$domain: String!
|
||||
) {
|
||||
validateAssertion(assertionResponse: $assertionResponse, domain: $domain)
|
||||
}
|
||||
`
|
||||
|
||||
const GET_USER_DATA = gql`
|
||||
{
|
||||
userData {
|
||||
id
|
||||
username
|
||||
role
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const validationSchema = Yup.object().shape({
|
||||
email: Yup.string().label('Email').required().email(),
|
||||
password: Yup.string().required('Password field is required'),
|
||||
rememberMe: Yup.boolean()
|
||||
})
|
||||
|
||||
const initialValues = {
|
||||
email: '',
|
||||
password: '',
|
||||
rememberMe: false
|
||||
}
|
||||
|
||||
const getErrorMsg = (formikErrors, formikTouched, mutationError) => {
|
||||
if (!formikErrors || !formikTouched) return null
|
||||
if (mutationError) return 'Invalid email/password combination'
|
||||
if (formikErrors.email && formikTouched.email) return formikErrors.email
|
||||
if (formikErrors.password && formikTouched.password)
|
||||
return formikErrors.password
|
||||
return null
|
||||
}
|
||||
|
||||
const LoginState = ({ state, dispatch, strategy }) => {
|
||||
const history = useHistory()
|
||||
const { setUserData } = useContext(AppContext)
|
||||
|
||||
const [login, { error: loginMutationError }] = useMutation(LOGIN)
|
||||
|
||||
const submitLogin = async (username, password, rememberMe) => {
|
||||
const options = {
|
||||
variables: {
|
||||
username,
|
||||
password
|
||||
}
|
||||
}
|
||||
const { data: loginResponse } = await login(options)
|
||||
|
||||
if (!loginResponse.login) return
|
||||
|
||||
return dispatch({
|
||||
type: loginResponse.login,
|
||||
payload: {
|
||||
clientField: username,
|
||||
passwordField: password,
|
||||
rememberMeField: rememberMe
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const [validateAssertion, { error: FIDOMutationError }] = useMutation(
|
||||
VALIDATE_ASSERTION,
|
||||
{
|
||||
onCompleted: ({ validateAssertion: success }) => success && getUserData()
|
||||
}
|
||||
)
|
||||
|
||||
const [assertionOptions, { error: assertionQueryError }] = useLazyQuery(
|
||||
GENERATE_ASSERTION,
|
||||
{
|
||||
onCompleted: ({ generateAssertionOptions: options }) => {
|
||||
startAssertion(options)
|
||||
.then(res => {
|
||||
validateAssertion({
|
||||
variables: {
|
||||
assertionResponse: res,
|
||||
domain: window.location.hostname
|
||||
}
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const [getUserData, { error: userDataQueryError }] = useLazyQuery(
|
||||
GET_USER_DATA,
|
||||
{
|
||||
onCompleted: ({ userData }) => {
|
||||
setUserData(userData)
|
||||
history.push('/')
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
return (
|
||||
<Formik
|
||||
validationSchema={validationSchema}
|
||||
initialValues={initialValues}
|
||||
onSubmit={values =>
|
||||
submitLogin(values.email, values.password, values.rememberMe)
|
||||
}>
|
||||
{({ errors, touched }) => (
|
||||
<Form id="login-form">
|
||||
<Field
|
||||
name="email"
|
||||
label="Email"
|
||||
size="lg"
|
||||
component={TextInput}
|
||||
fullWidth
|
||||
autoFocus
|
||||
className="-mt-4 mb-6"
|
||||
error={getErrorMsg(
|
||||
errors,
|
||||
touched,
|
||||
loginMutationError ||
|
||||
FIDOMutationError ||
|
||||
assertionQueryError ||
|
||||
userDataQueryError
|
||||
)}
|
||||
/>
|
||||
<Field
|
||||
name="password"
|
||||
size="lg"
|
||||
component={SecretInput}
|
||||
label="Password"
|
||||
fullWidth
|
||||
error={getErrorMsg(
|
||||
errors,
|
||||
touched,
|
||||
loginMutationError ||
|
||||
FIDOMutationError ||
|
||||
assertionQueryError ||
|
||||
userDataQueryError
|
||||
)}
|
||||
/>
|
||||
<div className="mt-9 flex">
|
||||
<Field
|
||||
name="rememberMe"
|
||||
className="-ml-2 transform-[scale(1.5)]"
|
||||
component={Checkbox}
|
||||
size="medium"
|
||||
/>
|
||||
<Label3>Keep me logged in</Label3>
|
||||
</div>
|
||||
<div className="mt-15">
|
||||
{getErrorMsg(
|
||||
errors,
|
||||
touched,
|
||||
loginMutationError ||
|
||||
FIDOMutationError ||
|
||||
assertionQueryError ||
|
||||
userDataQueryError
|
||||
) && (
|
||||
<P className="text-tomato">
|
||||
{getErrorMsg(
|
||||
errors,
|
||||
touched,
|
||||
loginMutationError ||
|
||||
FIDOMutationError ||
|
||||
assertionQueryError ||
|
||||
userDataQueryError
|
||||
)}
|
||||
</P>
|
||||
)}
|
||||
{strategy !== 'FIDO2FA' && (
|
||||
<Button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
return strategy === 'FIDOUsernameless'
|
||||
? assertionOptions({
|
||||
variables: { domain: window.location.hostname }
|
||||
})
|
||||
: dispatch({
|
||||
type: 'FIDO',
|
||||
payload: {}
|
||||
})
|
||||
}}
|
||||
buttonClassName="w-full"
|
||||
className="mb-3">
|
||||
I have a hardware key
|
||||
</Button>
|
||||
)}
|
||||
<Button type="submit" form="login-form" buttonClassName="w-full">
|
||||
Login
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
)
|
||||
}
|
||||
|
||||
export default LoginState
|
||||
Loading…
Add table
Add a link
Reference in a new issue