Skip to content

Commit e49e8d7

Browse files
fix: Detect autofill/paste in the email field and fireprechecks (#42396)
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
1 parent bf1b303 commit e49e8d7

File tree

3 files changed

+28
-3
lines changed

3 files changed

+28
-3
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { useEffect, useRef } from 'react'
2+
3+
export function usePrevious<T>(value: T): T | undefined {
4+
const ref = useRef<T | undefined>(undefined)
5+
6+
useEffect(() => {
7+
ref.current = value
8+
}, [value])
9+
10+
return ref.current
11+
}

frontend/src/lib/utils.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -850,13 +850,14 @@ export function isExternalLink(input: any): boolean {
850850
return !!input.trim().match(regexp)
851851
}
852852

853-
export function isEmail(string: string): boolean {
853+
export function isEmail(string: string, options?: { requireTLD?: boolean }): boolean {
854854
if (!string) {
855855
return false
856856
}
857857
// https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address
858-
const regexp =
859-
/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
858+
const regexp = options?.requireTLD
859+
? /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\.[a-zA-Z]{2,}$/
860+
: /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
860861
return !!string.match?.(regexp)
861862
}
862863

frontend/src/scenes/authentication/Login.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ import { getCookie } from 'lib/api'
1111
import { BridgePage } from 'lib/components/BridgePage/BridgePage'
1212
import { SSOEnforcedLoginButton, SocialLoginButtons } from 'lib/components/SocialLoginButton/SocialLoginButton'
1313
import { supportLogic } from 'lib/components/Support/supportLogic'
14+
import { usePrevious } from 'lib/hooks/usePrevious'
1415
import { LemonBanner } from 'lib/lemon-ui/LemonBanner'
1516
import { LemonField } from 'lib/lemon-ui/LemonField'
1617
import { Link } from 'lib/lemon-ui/Link'
18+
import { isEmail } from 'lib/utils'
1719
import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic'
1820
import { SceneExport } from 'scenes/sceneTypes'
1921
import { urls } from 'scenes/urls'
@@ -86,6 +88,7 @@ export function Login(): JSX.Element {
8688
const wasPasswordHiddenRef = useRef(isPasswordHidden)
8789

8890
const lastLoginMethod = getCookie(LAST_LOGIN_METHOD_COOKIE) as LoginMethod
91+
const prevEmail = usePrevious(login.email)
8992

9093
useEffect(() => {
9194
const wasPasswordHidden = wasPasswordHiddenRef.current
@@ -99,6 +102,16 @@ export function Login(): JSX.Element {
99102
}
100103
}, [isPasswordHidden, resetLogin])
101104

105+
// Trigger precheck for password manager autofill/paste (detected by large character delta)
106+
useEffect(() => {
107+
const charDelta = login.email.length - (prevEmail?.length ?? 0)
108+
const isAutofill = charDelta > 1
109+
110+
if (isAutofill && isEmail(login.email, { requireTLD: true }) && precheckResponse.status === 'pending') {
111+
precheck({ email: login.email })
112+
}
113+
}, [login.email, prevEmail, precheckResponse.status, precheck])
114+
102115
return (
103116
<BridgePage
104117
view="login"

0 commit comments

Comments
 (0)