Introducing VIDA Identity Stack: All of VIDA, in one place 🎉
For the complete documentation index, see llms.txt. This page is also available as Markdown.

WebView Camera Prerequisites

This page is for client mobile app developers who embed the VIDA Liveness Web SDK - or a VIDA-hosted verification page — inside an in-app WebView. It explains why the camera may fail to load in a WebView and how to make the app work.

Overview

The SDK uses the standard browser API navigator.mediaDevices.getUserMedia() to access the camera. Inside an in-app WebView, that call only succeeds when all three of the following are true:

  1. The app has been granted OS camera permission.

  2. The app forwards and grants the WebView's camera request in native code. This is the step most often missing.

  3. The page is served over a secure context (HTTPS).

If step 2 is not implemented, the camera fails silently — the SDK reports error 70001 (camera permission denied) even when the app itself already has camera permission.

This is a native integration requirement, not an SDK defect: the web layer cannot grant native or WebView permissions on its own.

Minimum WebView / Engine Version

Beyond permissions, the WebView engine has capability to run the SDK's face-detection engine (MediaPipe), which requires WebGL2 and WebAssembly.

  • Android: Android System WebView / Chromium ≥ 101.

  • iOS: the WebView uses the system WebKit, which stays current with the OS. iOS 14.3+ is required for getUserMedia in WKWebView.

Android OS (launch)
WebView shipped at launch
Updatable to ≥ 101?

Android 8 / 9

~58–69

Limited (Chromium dropped support after v109)

Android 10

~77

Yes - update via Play Store

Android 11

~83–85

Yes

Android 12+

~94+

Yes

Why this matters

Android System WebView updates separately from the OS. It is a Play Store component, so its version is independent of the Android version — a device on Android 10 shipped with WebView ~77 (released 2019) and stays there until someone updates it.

When the WebView is too old, the failure is easy to misread:

  • It cannot load WebGL2 / WASM, and may not reach the SDK's model/asset CDN.

  • The flow gets stuck on the loading spinner — the camera never opens.

  • No telemetry is captured for the session, so support sees an empty log.

The two failure modes look different, which lets you tell them apart at a glance:

What the user sees
Likely cause
Signal

Immediate error, no loading

Permission problem (see the three layers)

Error 70001

Loading spinner forever, then fails

Outdated WebView engine (< v101)

No error code; empty logs

How to check and update it (device side)

  1. Open the Play Store → search "Android System WebView" → Update. Also update Chrome, which provides the WebView engine on some devices.

  2. Or verify the installed version: Settings → Apps → Android System WebView.

  3. After updating, fully close and reopen the host app, then retry the flow.

The three permission layers

Granting permission at one layer does not propagate to the others.

Layer
Controlled by
How it's granted

OS / app permission

Android / iOS

User grants the host app camera access (runtime prompt / Settings).

WebView delegation

Your native app code

The app must explicitly handle and grant the WebView's camera permission request. There is no default behavior on Android.

Origin / site permission

The WebView engine

Result of getUserMedia() after layer 2 allows it.

In a normal mobile browser, layers 2 and 3 are handled for you. In an embedded WebView, layer 2 is your responsibility. Without it, getUserMedia() throws NotAllowedError → SDK error 70001.

Android (android.webkit.WebView)

1. Declare and request the OS camera permission

In AndroidManifest.xml:

Request the runtime permission before loading the page (Android 6+):

2. Grant the WebView's permission request (the critical step)

Set a WebChromeClient and handle onPermissionRequest. Without this, the WebView denies the camera by default:

3. Enable the required WebView settings

4. Serve over HTTPS

getUserMedia requires a secure context. Load the SDK/hosted page over https:// (not http://, and not a file:// page making cross-origin calls). Do not disable TLS validation.

Common Android pitfalls

  • No WebChromeClient.onPermissionRequest override → camera always denied. This is the most common cause.

  • Granting the WebView request before the app has the OS permission → still denied. Request OS permission first.

  • mediaPlaybackRequiresUserGesture = true (the default) can block the stream from starting.

  • Using a heavily customized or outdated WebView (System WebView not updated) that lacks WebRTC support.

iOS (WKWebView)

Use WKWebView. The legacy UIWebView does not support getUserMedia at all.

1. Declare the usage description

In Info.plist:

The OS prompts the user the first time the camera is accessed. Without this key, the app crashes on access.

2. Configure WKWebView for inline media capture

3. (iOS 15+) Auto-grant the WebView capture prompt if desired

On iOS 15+ you can implement WKUIDelegate to control the per-page capture permission instead of showing the WebView's own prompt:

4. Serve over HTTPS

Same secure-context requirement as Android. Avoid ATS exceptions that downgrade security.

Common iOS pitfalls

  • Using UIWebView instead of WKWebView → camera never works.

  • Missing NSCameraUsageDescription → app crashes when the camera is accessed.

  • allowsInlineMediaPlayback = false, or requiring a user gesture → the stream won't start.

  • The user previously denied OS camera permission to the app → it must be re-enabled in Settings → [App] → Camera. The WebView cannot override this.

Full sample code

Complete, drop-in samples with all required settings applied. Replace the placeholder URL with the VIDA flow / hosted link you were given.

1. Android

Android (MainActivity.java)

Changes vs. the basic snippets: added setMediaPlaybackRequiresUserGesture(false) (without it the camera stream can be blocked from starting), and onPermissionRequest now grants only RESOURCE_VIDEO_CAPTURE instead of every requested resource.

AndroidManifest.xml must also declare:

2. iOS

iOS (ViewController.swift)

Changes vs. the basic snippets: added mediaTypesRequiringUserActionForPlayback = [] (the iOS equivalent of Android's media-gesture setting) and the iOS 15+ capture-permission delegate.

Info.plist must also declare:

3. React Native

Use the react-native-webview package (v11+). The same three layers apply — the JS props below cover the WebView delegation and settings, but you still need the native OS permission and the manifest / Info.plist entries.

Prop
Purpose

mediaPlaybackRequiresUserAction={false}

Allow the camera stream to start without an extra tap (Android + iOS).

allowsInlineMediaPlayback

Inline (non-fullscreen) capture on iOS.

mediaCapturePermissionGrantType="grant"

Auto-grant the getUserMedia prompt on iOS 15+.

javaScriptEnabled, domStorageEnabled

JS + localStorage (SDK requirement).

On Android, react-native-webview automatically grants the WebView's camera request only if the app already holds the OS CAMERA permission — so you must request it before the WebView mounts.

Full sample (VidaWebView.jsx)

You must also add the native entries:

  • Android android/app/src/main/AndroidManifest.xml: <uses-permission android:name="android.permission.CAMERA" />

  • iOS ios/<App>/Info.plist: NSCameraUsageDescription (a string explaining camera use).

4. Flutter (flutter_inappwebview)

For camera / getUserMedia inside a Flutter WebView, use flutter_inappwebview (v6+) — it exposes the onPermissionRequest callback and media settings needed for the camera. (The official webview_flutter works too; see the note below.) Pair it with permission_handler for the OS-level prompt.

Setting / callback
Purpose

mediaPlaybackRequiresUserGesture: false

Allow the camera stream to start without a tap (Android + iOS).

allowsInlineMediaPlayback: true

Inline capture on iOS.

onPermissionRequest → GRANT

Grant the WebView's camera request (Android + iOS 15+).

iframeAllow: "camera; microphone"

Needed if the SDK runs inside an iframe on the page.

Full sample (vida_webview.dart)

You must also add the native entries:

  • Android android/app/src/main/AndroidManifest.xml: <uses-permission android:name="android.permission.CAMERA" />

  • iOS ios/Runner/Info.plist: NSCameraUsageDescription (a string explaining camera use).

Using the official webview_flutter instead? On Android you must grant the request explicitly:

And create the iOS controller with allowsInlineMediaPlayback: true + mediaTypesRequiringUserAction: {}.

How to tell which layer is failing

Symptom
Likely layer
Fix

SDK shows error 70001 immediately; no OS prompt ever appeared

Layer 2 (Android onPermissionRequest missing / iOS delegate denying)

Implement the native permission handler.

OS camera prompt appeared, user allowed, still fails

Layer 2 or secure context

Verify grant() is called for RESOURCE_VIDEO_CAPTURE; confirm HTTPS.

Stuck on the loading spinner; camera preview never appears, then fails. No logs captured for the session.

Outdated WebView engine (< v101) — can't load WebGL2/WASM or reach the model CDN

Update Android System WebView to ≥ 101 via Play Store (see Minimum WebView / engine version).

Works in mobile Safari/Chrome but not in the app

WebView config

Compare against the settings above (WKWebView / WebChromeClient).

Error 70003 (not supported) / 70005 (no camera)

Engine / device

Old WebView without WebRTC, or no camera device.

Worked once, now skips straight to a denied error

SDK caches an "allowed" flag in localStorage independent of real permission

Clear WebView storage, or re-grant in OS settings then reload.

The SDK shows its own "Allow camera access" screen with an Allow button. Inside a WebView this is a UI affordance only — it does not grant any OS, WebView, or browser permission. It simply records that the user is ready, then triggers the real getUserMedia() call.

The actual permission outcome is determined entirely by the three layers above. A user tapping "Allow" inside the SDK will not change device or WebView permission settings; the native prerequisites in this document must still be met.

Last updated