import { useMutation, useLazyQuery, gql } from "@apollo/client"; import { makeStyles } from '@mui/styles' 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' import styles from './shared.styles' const useStyles = makeStyles(styles) 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 classes = useStyles() 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 ( submitLogin(values.email, values.password, values.rememberMe) }> {({ errors, touched }) => (
Keep me logged in
{getErrorMsg( errors, touched, loginMutationError || FIDOMutationError || assertionQueryError || userDataQueryError ) && (

{getErrorMsg( errors, touched, loginMutationError || FIDOMutationError || assertionQueryError || userDataQueryError )}

)} {strategy !== 'FIDO2FA' && ( )}
)}
) } export default LoginState