import { useEffect, useState, useCallback } from 'react'
import { trpc } from '~/utils/trpc'
import { RCTResponderProps } from '~/components/RenderIOCall'
import { Field, Form, Formik } from 'formik'
import IVButton from '~/components/IVButton'
import IVDialog, { useDialogState } from '~/components/IVDialog'
import IVInputField from '~/components/IVInputField'
import MFAInput from '~/components/MFAInput'
import { tryLogin } from '~/utils/auth'
import GoogleIcon from '~/icons/compiled/Google'
import CheckCircleIcon from '~/icons/compiled/CheckCircleOutline'
import XCircleIcon from '~/icons/compiled/XCircle'
function InlineConfirmNotice(props: RCTResponderProps<'CONFIRM'>) {
const Icon = props.value ? CheckCircleIcon : XCircleIcon
return (
{props.value ? 'Identity confirmed' : 'Identity not confirmed'}
)
}
function ConfirmMFA({ onRespond, transactionId, context }) {
const challenge = trpc.useMutation(['auth.mfa.challenge'])
const verify = trpc.useMutation(['auth.mfa.verify'], {
onSuccess() {
onRespond(true)
},
})
const { mutate: challengeMfa } = challenge
useEffect(() => {
challengeMfa()
}, [challengeMfa])
return (
initialValues={{
code: '',
}}
onSubmit={async ({ code }) => {
if (!challenge.data || context === 'docs') return
verify.mutate({
code,
challengeId: challenge.data,
transactionId,
})
}}
validate={({ code }) => {
if (!code) {
return {
code: 'Please enter a code.',
}
}
}}
>
{({ isValid }) => (
)}
)
}
function ConfirmPassword({
onRespond,
email,
transactionId,
}: {
onRespond: (response: boolean) => void
email: string
transactionId?: string
}) {
const [loading, setLoading] = useState(false)
const [hasError, setHasError] = useState(false)
return (
initialValues={{
password: '',
}}
onSubmit={async ({ password }) => {
setHasError(false)
setLoading(true)
try {
const r = await tryLogin({
email,
password,
transactionId,
})
setLoading(false)
if (r.ok) {
onRespond(true)
} else {
setHasError(true)
}
} catch (error) {
console.error('Error confirming password', { error })
setHasError(true)
}
}}
validate={({ password }) => {
if (!password.length) {
return { password: 'Please enter your password.' }
}
}}
>
{({ isValid }) => (
)}
)
}
function ConfirmViaRedirect({
href,
label,
onRespond,
theme,
}: {
href: string
label: React.ReactNode
onRespond: (ok: boolean) => void
theme?: 'primary' | 'secondary' | 'danger' | 'plain'
}) {
return (
{
window.open(href, '_blank', 'popup,width=800,height=600')
}}
/>
onRespond(false)}
/>
)
}
export default function ConfirmIdentity(
props: RCTResponderProps<'CONFIRM_IDENTITY'>
) {
const session = trpc.useQuery(['auth.session.user'])
const authCheck = trpc.useQuery([
'auth.check',
{
email: session.data?.email || '',
transactionId: props.transaction?.id,
},
])
const confirm = trpc.useMutation(['auth.identity.confirm'])
const hasMfa = trpc.useQuery(['auth.mfa.has'])
const hasPassword = trpc.useQuery(['auth.password.has'])
const { onUpdatePendingReturnValue } = props
const dialog = useDialogState({
visible: true,
// non-modals are rendered within their parent component, not a
modal: props.context === 'transaction',
})
const { hide } = dialog
const onRespond = useCallback(
(value: boolean) => {
onUpdatePendingReturnValue(value)
if (props.context !== 'docs') hide()
},
[onUpdatePendingReturnValue, props.context, hide]
)
const { mutate: doConfirm } = confirm
const { refetch: refetchAuth } = authCheck
useEffect(() => {
if (authCheck.data?.identityConfirmed && props.transaction) {
doConfirm(
{ transactionId: props.transaction?.id },
{
onSuccess(response) {
if (response === true) {
onUpdatePendingReturnValue(true)
} else {
refetchAuth()
}
},
}
)
}
}, [
authCheck.data,
onUpdatePendingReturnValue,
props.transaction,
doConfirm,
refetchAuth,
])
// show inline result of the confirmation process
if (props.shouldUseAppendUi && typeof props.value === 'boolean') {
return
}
if (props.context !== 'docs') {
if (
!props.transaction ||
authCheck.isLoading ||
confirm.isLoading ||
authCheck.data?.identityConfirmed
) {
// avoid flash of modal until we know we need a confirmation
return null
}
}
let confirmComponent: React.ReactNode
if (props.context === 'docs' || hasMfa.data) {
confirmComponent = (
)
} else if (authCheck.data?.sso) {
const { workosOrganizationId } = authCheck.data.sso
const paramObj = {
workosOrganizationId,
}
if (props.transaction?.id) {
paramObj['transactionId'] = props.transaction.id
}
const params = new URLSearchParams(paramObj)
confirmComponent = (
)
} else if (hasPassword.data) {
confirmComponent = (
)
} else {
let href = `/api/auth/sso/sign-in-with-google`
if (props.transaction?.id) {
const params = new URLSearchParams({
transactionId: props.transaction?.id || '',
})
href += `?${params}`
}
confirmComponent = (
Continue with Google
}
onRespond={onRespond}
/>
)
}
return (
Please confirm your identity to continue
{props.label && {props.label}
}
{confirmComponent}
)
}