Turnstile no longer working inside of iOS apps (in WKWebView)

As of recently, users of our iOS app can no longer complete captcha requests using Turnstile. It spinns forever, and after a while also shows the following message:

Verification is taking longer than expected. Check your Internet connection and refresh the page if the issue persists.

This has been observed on multiple iOS devices, on multiple internet connections, in multiple countries. We have yet to find a single iOS device where it works. It does work reliably on Android, and on our web platform.

The following is the code that we are using in our React Native project:

import React, { useCallback } from 'react'
import WebView, { WebViewMessageEvent } from 'react-native-webview'
import { VStack } from 'react-stacked'

import { CLOUDFLARE_TURNSTILE_SITE_KEY } from '../lib/config'

// https://developers.cloudflare.com/turnstile/get-started/client-side-rendering/#widget-size
const size = { width: 300, height: 65 }

const html = `<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>CAPTCHA</title>
    <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
    <style>
      html, body {
        margin: 0;
        padding: 0;
      }

      body {
        display: flex;
        align-items: center;
        justify-content: center;

        min-height: ${size.height}px;
        min-width: ${size.width}px;

        background-image: url("data:image/svg+xml,%3Csvg width='32' height='32' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E.a%7Btransform-origin:center;animation:b .6s linear infinite%7D@keyframes b%7B100%25%7Btransform:rotate(360deg)%7D%7D%3C/style%3E%3Cpath class='a' d='M2,12A11.2,11.2,0,0,1,13,1.05C12.67,1,12.34,1,12,1a11,11,0,0,0,0,22c.34,0,.67,0,1-.05C6,23,2,17.74,2,12Z'/%3E%3C/svg%3E");
        background-position: center;
        background-repeat: no-repeat;
      }
    </style>
  </head>
  <body>
    <script>
      function javascriptCallback (token) {
        window.ReactNativeWebView.postMessage(token || '')
      }
    </script>

    <div class="cf-turnstile" data-sitekey="${CLOUDFLARE_TURNSTILE_SITE_KEY}" data-callback="javascriptCallback"></div>
  </body>
</html>
`

interface CaptchaViewProps {
  onFailure: () => void
  onSuccess: (token: string) => void
}

const CaptchaView: React.FC<CaptchaViewProps> = ({ onFailure, onSuccess }) => {
  const handleMessage = useCallback((event: WebViewMessageEvent) => {
    if (event.nativeEvent.data !== '') {
      onSuccess(event.nativeEvent.data)
    } else {
      onFailure()
    }
  }, [onFailure, onSuccess])

  return (
    <VStack {...size}>
      <WebView onMessage={handleMessage} source={{ baseUrl: 'https://ourwebapp.com', html }} style={size} />
    </VStack>
  )
}

export default CaptchaView

The documentation was updated on April 2nd and now clarifies that webviews also aren’t supported :anguished:

I am getting this exact same error where Turnstile is no longer working ONLY inside of iOS apps (also using React Native WebView). It seems to be working on iOS browser, android browser AND app, and on desktop browser.

Same issue here. On the documentation it states that it should be implemented with web views on native Apps. On Android its working, but on iOS it says “Verification is taking longer than expected…”. Does anyone have any update on this issue?

I was able to solve the issue I had by setting originWhitelist, like this:

        <WebView
          source={{
            uri: forgotPasswordURL,
          }}
          ref={webviewRef}
          mixedContentMode="always"
          originWhitelist={["*"]}
         ....
1 Like

Also observing this. All sites using Turnstile fail to load in our in-app webview (including our own content if enabled)

This is a big problem for us, would appreciate some guidance on whether we should expect Cloudflare to support WKWebView.

1 Like

After more investigations, we found out that it’s only about:blank and about:srcdoc that are required to be allowlisted to make Turnstile work.

Examples :

  • originWhitelist={['http://', 'https://', 'about:']}
  • onShouldStartLoadWithRequest={event => ['challenges.cloudflare.com', 'about:blank', 'about:srcdoc'].some(url => event.url.includes(url))}

I created a package react-native-turnstile that uses a hosted instance of Next.js in conjunction with a RN Webview: react-native-turnstile - npm