diff --git a/src/components/Global/PostSignupActionManager/index.tsx b/src/components/Global/PostSignupActionManager/index.tsx index b58d6143f6..aa7b1f8865 100644 --- a/src/components/Global/PostSignupActionManager/index.tsx +++ b/src/components/Global/PostSignupActionManager/index.tsx @@ -6,8 +6,6 @@ import { useEffect, useState } from 'react' import ActionModal from '../ActionModal' import { POST_SIGNUP_ACTIONS } from './post-signup-action.consts' import { type IconName } from '../Icons/Icon' -import { useAuth } from '@/context/authContext' -import { isUserKycVerified } from '@/constants/kyc.consts' export const PostSignupActionManager = ({ onActionModalVisibilityChange, @@ -23,11 +21,10 @@ export const PostSignupActionManager = ({ action: () => void } | null>(null) const router = useRouter() - const { user } = useAuth() - const checkClaimModalAfterKYC = () => { + const checkForPostSignupAction = () => { const redirectUrl = getRedirectUrl() - if (isUserKycVerified(user?.user) && redirectUrl) { + if (redirectUrl) { const matchedAction = POST_SIGNUP_ACTIONS.find((action) => action.pathPattern.test(redirectUrl)) if (matchedAction) { setActionConfig({ @@ -44,8 +41,8 @@ export const PostSignupActionManager = ({ } useEffect(() => { - checkClaimModalAfterKYC() - }, [router, user]) + checkForPostSignupAction() + }, [router]) useEffect(() => { onActionModalVisibilityChange(showModal) diff --git a/src/hooks/useZeroDev.ts b/src/hooks/useZeroDev.ts index adff7930de..c1870c0ecb 100644 --- a/src/hooks/useZeroDev.ts +++ b/src/hooks/useZeroDev.ts @@ -129,35 +129,52 @@ export const useZeroDev = () => { } } - // login function - const handleLogin = async () => { - dispatch(zerodevActions.setIsLoggingIn(true)) - try { - const passkeyServerHeaders: Record = {} + // TODO: Consider implementing conditional mediation (WebAuthn autofill) for login. + // Currently, discoverable credential requests cause conflicts on Samsung devices with + // dual passkey providers (Google Credential Manager + Samsung Pass). Conditional mediation + // surfaces passkeys from ALL providers in the autofill UI, bypassing provider priority issues. + // See: https://web.dev/articles/passkey-form-autofill + const _attemptLogin = async () => { + const passkeyServerHeaders: Record = {} - if (user?.user?.username) { - passkeyServerHeaders['x-username'] = user.user.username - } + if (user?.user?.username) { + passkeyServerHeaders['x-username'] = user.user.username + } - const webAuthnKey = await toWebAuthnKey({ - passkeyName: '[]', - passkeyServerUrl: consts.PASSKEY_SERVER_URL as string, - mode: WebAuthnMode.Login, - passkeyServerHeaders, - rpID: window.location.hostname.replace(/^www\./, ''), - }) + const webAuthnKey = await toWebAuthnKey({ + passkeyName: '[]', + passkeyServerUrl: consts.PASSKEY_SERVER_URL as string, + mode: WebAuthnMode.Login, + passkeyServerHeaders, + rpID: window.location.hostname.replace(/^www\./, ''), + }) - setWebAuthnKey(webAuthnKey) - saveToCookie(WEB_AUTHN_COOKIE_KEY, webAuthnKey, 90) + setWebAuthnKey(webAuthnKey) + saveToCookie(WEB_AUTHN_COOKIE_KEY, webAuthnKey, 90) + } + + const handleLogin = async () => { + dispatch(zerodevActions.setIsLoggingIn(true)) + try { + await _attemptLogin() } catch (e) { const error = e as Error if (error.name === 'NotAllowedError') { - // User cancelled - no state was saved, just let them retry - dispatch(zerodevActions.setIsLoggingIn(false)) - throw new PasskeyError( - 'Login was canceled or no passkey found. Please try again or register.', - 'LOGIN_CANCELED' - ) + // On devices with multiple passkey providers (e.g. Samsung with Google + Samsung Pass), + // the first provider may intercept and report "no passkeys" while the actual passkey + // lives in the other provider. Auto-retry once to give the second provider a chance. + console.warn('[useZeroDev] Login NotAllowedError, retrying once for multi-provider devices') + try { + await _attemptLogin() + return // retry succeeded + } catch (retryError) { + // retry also failed — throw user-facing error + dispatch(zerodevActions.setIsLoggingIn(false)) + throw new PasskeyError( + 'Login was canceled or no passkey found. Please try again or register.', + 'LOGIN_CANCELED' + ) + } } // Other login errors - clear any stale state