sentry errors for captcha web views and registration attempts (#3761)
* sentry errors for captcha web views * include handles with errors * log all registration request failures * rm * use a better trigger for web captcha errors * add another trigger for recording a possible signup error * unknown error type * don't needlessly log on href errors * honestly i probably cant always do a captcha in 20 seconds * rm log * timeout on back * remove unnecessary colonszio/stable
parent
81ae7e425d
commit
b8d8bec388
|
@ -26,7 +26,7 @@ export function CaptchaWebView({
|
||||||
stateParam: string
|
stateParam: string
|
||||||
state?: SignupState
|
state?: SignupState
|
||||||
onSuccess: (code: string) => void
|
onSuccess: (code: string) => void
|
||||||
onError: () => void
|
onError: (error: unknown) => void
|
||||||
}) {
|
}) {
|
||||||
const redirectHost = React.useMemo(() => {
|
const redirectHost = React.useMemo(() => {
|
||||||
if (!state?.serviceUrl) return 'bsky.app'
|
if (!state?.serviceUrl) return 'bsky.app'
|
||||||
|
@ -56,7 +56,7 @@ export function CaptchaWebView({
|
||||||
|
|
||||||
const code = urlp.searchParams.get('code')
|
const code = urlp.searchParams.get('code')
|
||||||
if (urlp.searchParams.get('state') !== stateParam || !code) {
|
if (urlp.searchParams.get('state') !== stateParam || !code) {
|
||||||
onError()
|
onError({error: 'Invalid state or code'})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,6 +74,12 @@ export function CaptchaWebView({
|
||||||
onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
|
onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
|
||||||
onNavigationStateChange={onNavigationStateChange}
|
onNavigationStateChange={onNavigationStateChange}
|
||||||
scrollEnabled={false}
|
scrollEnabled={false}
|
||||||
|
onError={e => {
|
||||||
|
onError(e.nativeEvent)
|
||||||
|
}}
|
||||||
|
onHttpError={e => {
|
||||||
|
onError(e.nativeEvent)
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,20 @@ export function CaptchaWebView({
|
||||||
url: string
|
url: string
|
||||||
stateParam: string
|
stateParam: string
|
||||||
onSuccess: (code: string) => void
|
onSuccess: (code: string) => void
|
||||||
onError: () => void
|
onError: (error: unknown) => void
|
||||||
}) {
|
}) {
|
||||||
|
React.useEffect(() => {
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
onError({
|
||||||
|
errorMessage: 'User did not complete the captcha within 30 seconds',
|
||||||
|
})
|
||||||
|
}, 30e3)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearTimeout(timeout)
|
||||||
|
}
|
||||||
|
}, [onError])
|
||||||
|
|
||||||
const onLoad = React.useCallback(() => {
|
const onLoad = React.useCallback(() => {
|
||||||
// @ts-ignore web
|
// @ts-ignore web
|
||||||
const frame: HTMLIFrameElement = document.getElementById(
|
const frame: HTMLIFrameElement = document.getElementById(
|
||||||
|
@ -32,12 +44,14 @@ export function CaptchaWebView({
|
||||||
|
|
||||||
const code = urlp.searchParams.get('code')
|
const code = urlp.searchParams.get('code')
|
||||||
if (urlp.searchParams.get('state') !== stateParam || !code) {
|
if (urlp.searchParams.get('state') !== stateParam || !code) {
|
||||||
onError()
|
onError({error: 'Invalid state or code'})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
onSuccess(code)
|
onSuccess(code)
|
||||||
} catch (e) {
|
} catch (e: unknown) {
|
||||||
// We don't need to handle this
|
// We don't actually want to record an error here, because this will happen quite a bit. We will only be able to
|
||||||
|
// get hte href of the iframe if it's on our domain, so all the hcaptcha requests will throw here, although it's
|
||||||
|
// harmless. Our other indicators of time-to-complete and back press should be more reliable in catching issues.
|
||||||
}
|
}
|
||||||
}, [stateParam, onSuccess, onError])
|
}, [stateParam, onSuccess, onError])
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {useLingui} from '@lingui/react'
|
||||||
import {nanoid} from 'nanoid/non-secure'
|
import {nanoid} from 'nanoid/non-secure'
|
||||||
|
|
||||||
import {createFullHandle} from '#/lib/strings/handles'
|
import {createFullHandle} from '#/lib/strings/handles'
|
||||||
|
import {logger} from '#/logger'
|
||||||
import {ScreenTransition} from '#/screens/Login/ScreenTransition'
|
import {ScreenTransition} from '#/screens/Login/ScreenTransition'
|
||||||
import {useSignupContext, useSubmitSignup} from '#/screens/Signup/state'
|
import {useSignupContext, useSubmitSignup} from '#/screens/Signup/state'
|
||||||
import {CaptchaWebView} from '#/screens/Signup/StepCaptcha/CaptchaWebView'
|
import {CaptchaWebView} from '#/screens/Signup/StepCaptcha/CaptchaWebView'
|
||||||
|
@ -43,12 +44,19 @@ export function StepCaptcha() {
|
||||||
[submit],
|
[submit],
|
||||||
)
|
)
|
||||||
|
|
||||||
const onError = React.useCallback(() => {
|
const onError = React.useCallback(
|
||||||
dispatch({
|
(error?: unknown) => {
|
||||||
type: 'setError',
|
dispatch({
|
||||||
value: _(msg`Error receiving captcha response.`),
|
type: 'setError',
|
||||||
})
|
value: _(msg`Error receiving captcha response.`),
|
||||||
}, [_, dispatch])
|
})
|
||||||
|
logger.error('Signup Flow Error', {
|
||||||
|
registrationHandle: state.handle,
|
||||||
|
error,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
[_, dispatch, state.handle],
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScreenTransition>
|
<ScreenTransition>
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {useAnalytics} from '#/lib/analytics/analytics'
|
||||||
import {FEEDBACK_FORM_URL} from '#/lib/constants'
|
import {FEEDBACK_FORM_URL} from '#/lib/constants'
|
||||||
import {logEvent} from '#/lib/statsig/statsig'
|
import {logEvent} from '#/lib/statsig/statsig'
|
||||||
import {createFullHandle} from '#/lib/strings/handles'
|
import {createFullHandle} from '#/lib/strings/handles'
|
||||||
|
import {logger} from '#/logger'
|
||||||
import {useServiceQuery} from '#/state/queries/service'
|
import {useServiceQuery} from '#/state/queries/service'
|
||||||
import {useAgent} from '#/state/session'
|
import {useAgent} from '#/state/session'
|
||||||
import {LoggedOutLayout} from '#/view/com/util/layouts/LoggedOutLayout'
|
import {LoggedOutLayout} from '#/view/com/util/layouts/LoggedOutLayout'
|
||||||
|
@ -119,11 +120,19 @@ export function Signup({onPressBack}: {onPressBack: () => void}) {
|
||||||
|
|
||||||
const onBackPress = React.useCallback(() => {
|
const onBackPress = React.useCallback(() => {
|
||||||
if (state.activeStep !== SignupStep.INFO) {
|
if (state.activeStep !== SignupStep.INFO) {
|
||||||
|
if (state.activeStep === SignupStep.CAPTCHA) {
|
||||||
|
logger.error('Signup Flow Error', {
|
||||||
|
errorMessage:
|
||||||
|
'User went back from captcha step. Possibly encountered an error.',
|
||||||
|
registrationHandle: state.handle,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
dispatch({type: 'prev'})
|
dispatch({type: 'prev'})
|
||||||
} else {
|
} else {
|
||||||
onPressBack()
|
onPressBack()
|
||||||
}
|
}
|
||||||
}, [onPressBack, state.activeStep])
|
}, [onPressBack, state.activeStep, state.handle])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SignupContext.Provider value={{state, dispatch}}>
|
<SignupContext.Provider value={{state, dispatch}}>
|
||||||
|
|
|
@ -246,6 +246,10 @@ export function useSubmitSignup({
|
||||||
!verificationCode
|
!verificationCode
|
||||||
) {
|
) {
|
||||||
dispatch({type: 'setStep', value: SignupStep.CAPTCHA})
|
dispatch({type: 'setStep', value: SignupStep.CAPTCHA})
|
||||||
|
logger.error('Signup Flow Error', {
|
||||||
|
errorMessage: 'Verification captcha code was not set.',
|
||||||
|
registrationHandle: state.handle,
|
||||||
|
})
|
||||||
return dispatch({
|
return dispatch({
|
||||||
type: 'setError',
|
type: 'setError',
|
||||||
value: _(msg`Please complete the verification captcha.`),
|
value: _(msg`Please complete the verification captcha.`),
|
||||||
|
@ -282,20 +286,17 @@ export function useSubmitSignup({
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([400, 429].includes(e.status)) {
|
|
||||||
logger.warn('Failed to create account', {message: e})
|
|
||||||
} else {
|
|
||||||
logger.error(`Failed to create account (${e.status} status)`, {
|
|
||||||
message: e,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const error = cleanError(errMsg)
|
const error = cleanError(errMsg)
|
||||||
const isHandleError = error.toLowerCase().includes('handle')
|
const isHandleError = error.toLowerCase().includes('handle')
|
||||||
|
|
||||||
dispatch({type: 'setIsLoading', value: false})
|
dispatch({type: 'setIsLoading', value: false})
|
||||||
dispatch({type: 'setError', value: cleanError(errMsg)})
|
dispatch({type: 'setError', value: error})
|
||||||
dispatch({type: 'setStep', value: isHandleError ? 2 : 1})
|
dispatch({type: 'setStep', value: isHandleError ? 2 : 1})
|
||||||
|
|
||||||
|
logger.error('Signup Flow Error', {
|
||||||
|
errorMessage: error,
|
||||||
|
registrationHandle: state.handle,
|
||||||
|
})
|
||||||
} finally {
|
} finally {
|
||||||
dispatch({type: 'setIsLoading', value: false})
|
dispatch({type: 'setIsLoading', value: false})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue