react-google-recaptcha icon indicating copy to clipboard operation
react-google-recaptcha copied to clipboard

executeAsync can only be called once

Open MethodenMann opened this issue 5 years ago • 13 comments

I use the executeAsync. The first time i call it there is no problem and i receive a token. But the second time the call hangs and i dont get a token nor an error.

i created a codepen that reproduces the behavior: https://codesandbox.io/s/react-google-recaptcha-210-example-invisible-forked-wkk89?file=/src/index.js:441-453

Not that after the first call "onSubmitAsync" is printed, but the executeAsync never returns.

If i would call reset afterwards it works, but i reckon i get to many puzzles to solve with a reset. Plus the reset is visible in the UI.

react-google-recaptcha version: 2.1.0 react-async-script version: 1.2.0

MethodenMann avatar Sep 23 '20 09:09 MethodenMann

@MethodenMann Thanks for the detailed and informative issue. And THANK YOU for the codesandbox.io example!

I was able to dig into this a bit. There is definitely some sort of internal "throttle" to the grecaptcha.execute command. Google apparently doesn't want their systems spammed for tokens/responses.

Both of react-google-recaptcha's methods execute and executeAsync have the same issue with this "throttling".

But there is a method on the google recaptcha object itself that can give you the recaptcha's current valid response/token grecaptcha.getResponse.

  • https://developers.google.com/recaptcha/docs/invisible#js_api

react-google-recaptcha exposes this api via our getValue method. https://github.com/dozoisch/react-google-recaptcha/blob/5ec0e2fa9b492f338f82a59755b735840d9f348a/src/recaptcha.js#L13-L16

Let me know if this is something you can use to help solve your use case.

Cheers.

hartzis avatar Sep 27 '20 21:09 hartzis

Sorry it took me so long to get back to this. Our client generates the token and then does a login request with this token. If the request fails and the user tries another password, a new token should be created for further login attempts. We could use getValue to get the current valid token for further requests. But the problem is, that this token is already verified in the backend and therefore not valid anymore. A token can only be verified once once to prevent replay attacks (https://developers.google.com/recaptcha/docs/verify) So it would really be neat to just get a new token on the client site. The only other solution i can think of, is to remember a valid token verification in the backend. Hope i did explain the problem properly.

MethodenMann avatar Oct 07 '20 14:10 MethodenMann

You can run grecaptcha.reset() to reset the recaptcha so that you don't need to wait until you can call grecaptcha.execute() again.

azinod avatar Oct 23 '20 23:10 azinod

Bump

hafezoa avatar Dec 02 '20 20:12 hafezoa

@hafezoa seems like the last comment in the thread answers the question. If you've tried what azinod is suggesting and it doesn't work please provide information about it.

dozoisch avatar Dec 02 '20 22:12 dozoisch

@dozoisch it doesn't work..

ScriptArtist avatar Dec 04 '20 18:12 ScriptArtist

I found only one solution to remove react element from DOM and reinit again:

initForm() {
  setIsRecaptchaEnable(true);
}

onCaptchaUpdate() {
   setIsRecaptchaEnable(false);
}

{ isRecaptchaEnable &&
<reCaptcha ... />
}

ScriptArtist avatar Dec 05 '20 11:12 ScriptArtist

I had a similar problem. @ScriptArtist's solution seems to work.

x13machine avatar Dec 31 '20 04:12 x13machine

one problem is that recaptcha continually creates a new a div element in the background.

x13machine avatar Dec 31 '20 06:12 x13machine

Actually the solution of @azinod seems to work pretty well.

https://codesandbox.io/s/react-google-recaptcha-210-example-invisible-forked-u35ii

thuraua avatar Jan 26 '21 18:01 thuraua

Just wanted to share that @azinod 's answer also works for executeAsync()!

marnixhoh avatar Aug 08 '21 18:08 marnixhoh

The recaptcha ref is null after ref.current.executeAsync(). The executeAsync call returns a token but it throws if I try to do ref.current.reset() after it as I am now trying to access property of null. Any suggestions?

akshay-nm avatar May 06 '22 09:05 akshay-nm

Not sure, but I think you have to use useCallback for update your ref when re-sumit…

Example:

const recaptchaRef = createRef()

const onSubmit = useCallback(async () => {
  const token = await recaptchaRef.current?.executeAsync()
  recaptchaRef.current?.reset()
}, [recaptchaRef])

l2aelba avatar Jul 13 '23 11:07 l2aelba