import { useMutation, useLazyQuery, gql } from '@apollo/client' import { startAssertion } from '@simplewebauthn/browser' import { Field, Form, Formik } from 'formik' import React, { useContext } from 'react' import { useLocation } from 'wouter' import { Label3, P } from '../../components/typography' import * as Yup from 'yup' import AppContext from '../../AppContext' import { Button } from '../../components/buttons' import { Checkbox, SecretInput, TextInput, } from '../../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 = ({ dispatch, strategy }) => { const [, navigate] = useLocation() 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) navigate('/') }, }, ) 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