about summary refs log tree commit diff
path: root/src/screens/Signup/StepCaptcha/CaptchaWebView.tsx
diff options
context:
space:
mode:
authorSamuel Newman <mozzius@protonmail.com>2024-03-20 23:29:24 +0000
committerGitHub <noreply@github.com>2024-03-20 23:29:24 +0000
commitc649ee1afa80f71f108187df5671ae85eeaeed99 (patch)
treeadb5227f58811d0fe4af023184f9ffd71f66f463 /src/screens/Signup/StepCaptcha/CaptchaWebView.tsx
parent8ad813cd86c74a987cb81f5278c2eabbe8193db8 (diff)
parentd2d4d3a09206b52fe78018b89f82471c3dd91c8a (diff)
downloadvoidsky-c649ee1afa80f71f108187df5671ae85eeaeed99.tar.zst
Merge pull request #3217 from bluesky-social/samuel/alf-login
Use ALF for login & signup flow
Diffstat (limited to 'src/screens/Signup/StepCaptcha/CaptchaWebView.tsx')
-rw-r--r--src/screens/Signup/StepCaptcha/CaptchaWebView.tsx87
1 files changed, 87 insertions, 0 deletions
diff --git a/src/screens/Signup/StepCaptcha/CaptchaWebView.tsx b/src/screens/Signup/StepCaptcha/CaptchaWebView.tsx
new file mode 100644
index 000000000..50918c4ce
--- /dev/null
+++ b/src/screens/Signup/StepCaptcha/CaptchaWebView.tsx
@@ -0,0 +1,87 @@
+import React from 'react'
+import {StyleSheet} from 'react-native'
+import {WebView, WebViewNavigation} from 'react-native-webview'
+import {ShouldStartLoadRequest} from 'react-native-webview/lib/WebViewTypes'
+
+import {SignupState} from '#/screens/Signup/state'
+
+const ALLOWED_HOSTS = [
+  'bsky.social',
+  'bsky.app',
+  'staging.bsky.app',
+  'staging.bsky.dev',
+  'js.hcaptcha.com',
+  'newassets.hcaptcha.com',
+  'api2.hcaptcha.com',
+]
+
+export function CaptchaWebView({
+  url,
+  stateParam,
+  state,
+  onSuccess,
+  onError,
+}: {
+  url: string
+  stateParam: string
+  state?: SignupState
+  onSuccess: (code: string) => void
+  onError: () => void
+}) {
+  const redirectHost = React.useMemo(() => {
+    if (!state?.serviceUrl) return 'bsky.app'
+
+    return state?.serviceUrl &&
+      new URL(state?.serviceUrl).host === 'staging.bsky.dev'
+      ? 'staging.bsky.app'
+      : 'bsky.app'
+  }, [state?.serviceUrl])
+
+  const wasSuccessful = React.useRef(false)
+
+  const onShouldStartLoadWithRequest = React.useCallback(
+    (event: ShouldStartLoadRequest) => {
+      const urlp = new URL(event.url)
+      return ALLOWED_HOSTS.includes(urlp.host)
+    },
+    [],
+  )
+
+  const onNavigationStateChange = React.useCallback(
+    (e: WebViewNavigation) => {
+      if (wasSuccessful.current) return
+
+      const urlp = new URL(e.url)
+      if (urlp.host !== redirectHost) return
+
+      const code = urlp.searchParams.get('code')
+      if (urlp.searchParams.get('state') !== stateParam || !code) {
+        onError()
+        return
+      }
+
+      wasSuccessful.current = true
+      onSuccess(code)
+    },
+    [redirectHost, stateParam, onSuccess, onError],
+  )
+
+  return (
+    <WebView
+      source={{uri: url}}
+      javaScriptEnabled
+      style={styles.webview}
+      onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
+      onNavigationStateChange={onNavigationStateChange}
+      scrollEnabled={false}
+    />
+  )
+}
+
+const styles = StyleSheet.create({
+  webview: {
+    flex: 1,
+    backgroundColor: 'transparent',
+    borderRadius: 10,
+  },
+})