Dentro de un WebView, una página que pide la cámara o la ubicación debe pasar dos puertas separadas — el permiso del SO y el permiso del WebView — y en Android el valor por defecto es denegar la segunda si la app no implementa el callback. Peor aún, el WebView de Android da a la app contenedora el control total del manejo de permisos web, sin el consentimiento por sitio de un navegador real: una vez concedido el permiso del SO, la app puede compartir la ubicación o la cámara con cualquier página del WebView sin otro aviso. En iOS, WKWebView añade un segundo aviso encima del de la app, y los navegadores integrados como TikTok e Instagram no tienen soporte oficial. Un runtime propio del host ofrece un único intermediario de permisos que alinea el consentimiento nativo y web, aplica concesiones por origen, exige contexto seguro y muestra un solo aviso consistente.
Dos puertas, y la segunda dice no por defecto
En Android, una página que llama a getUserMedia o navigator.geolocation choca con dos comprobaciones. Primero el permiso del SO — CÁMARA, RECORD_AUDIO o ubicación declarados en el manifest y concedidos en runtime (API 23+). Luego el permiso del WebView: aunque el SO ya concedió, la página llega a la app como WebChromeClient.onPermissionRequest (cámara/mic) o onGeolocationPermissionsShowPrompt (ubicación), y la respuesta por defecto es no. Si no implementas el callback, el síntoma es desconcertante — permiso concedido, getUserMedia llamado, no pasa nada, sin diálogo, una promesa rechazada. La ubicación solo se llama para orígenes seguros en apps destinadas a Android 7.0+, así que una página HTTP se deniega antes de que alguien toque algo.
Sin consentimiento por sitio, a diferencia de un navegador
Un estudio de 2025 sobre la ejecución de permisos web en el WebView de Android muestra la brecha estructural: los navegadores tradicionales avisan por sitio y dejan al usuario conceder o denegar cada uno, pero el WebView de Android delega la decisión entera a la app contenedora. Si la app implementa el callback, puede conceder o denegar para cualquier origen, y no hay memoria por sitio ni control visible para el usuario. El riesgo más profundo es que, una vez concedido el permiso de ubicación del SO a la app, la app puede entregar esa ubicación a cualquier sitio cargado en el WebView sin otro consentimiento del usuario — el usuario no puede ver qué página accede a qué. Los desarrolladores deben construir esa UX de consentimiento ellos mismos, o no existe.
iOS: un segundo aviso, y degradaciones silenciosas
En iOS, el acceso a cámara y micrófono de WKWebView exige que la app implemente requestMediaCapturePermissionFor (iOS 15+) y los entitlements correspondientes; antes de eso, WebRTC simplemente no estaba disponible en WKWebView. Aun implementado, el usuario ve dos avisos — uno de la app, uno del sitio — y la solución documentada es que la app omita el segundo vía un delegate, lo que significa que la elección por página del usuario la decide la app, no la página. Cuando la página se carga mediante un custom scheme handler, el aviso muestra el nombre de la app como origen, lo que confunde a los usuarios. Los navegadores integrados (Instagram, TikTok) llevan restricciones adicionales y UI rota y no tienen soporte explícito.
Qué pierdes sin un intermediario de permisos
- Una única fuente de verdad. Los estados de permisos nativos y web viven en lugares distintos; se desalinean y contradicen.
- Control por origen. El WebView de Android no tiene memoria de concesión por sitio; la app concede todo o deniega todo.
- Visibilidad para el usuario. El usuario no puede saber qué página accede a la cámara o la ubicación, porque el consentimiento vive en el código de la app.
- Contexto seguro obligatorio. Una página HTTP pierde la ubicación en silencio en Android moderno, sin razón visible.
- Un aviso consistente. iOS avisa dos veces; los navegadores integrados se rompen por completo.
La solución del runtime
Un runtime propio del host ofrece un único intermediario de permisos por debajo de la página. Mantiene un almacén de consentimiento unificado para nativo y web, aplica concesiones por origen como un navegador, exige contexto seguro antes de ofrecer cualquier API sensible, y presenta un solo aviso consistente y legible que nombra la página y el recurso. La concesión del SO de la app y la petición web de la página se reconcilian en un solo lugar, así el consentimiento es visible, revocable y nunca se comparte en silencio con un origen desconocido.
| Dimensión | WebView | Runtime propio del host |
|---|---|---|
| Modelo de consentimiento | La app decide todo, sin memoria por sitio | Almacén unificado, concesiones por origen |
| Puerta 2 en Android | onPermissionRequest deniega por defecto | Intermediario concede por origen, visible |
| Avisos en iOS | Dos avisos, la app puede omitir el del sitio | Un solo aviso consistente |
| Contexto seguro | Ubicación HTTP denegada en silencio | Obligatorio, explicado |
| Visibilidad para el usuario | No se ve qué página accede a qué | El aviso nombra página y recurso |
| Navegadores integrados | Sin soporte, UI rota | Un runtime, soporte en todas partes |
Un navegador gana confianza haciendo la pregunta correcta, a la parte correcta, en el momento correcto. Un WebView de fábrica empuja esa decisión a código de app opaco y luego dice no por defecto. Un runtime recupera el contrato de consentimiento del navegador — por origen, visible y revocable — así una página que necesita la cámara obtiene un aviso que el usuario entiende de verdad.
Fuentes
- Android Developers — WebChromeClient.onPermissionRequest y PermissionRequest: la app debe implementar el callback o todas las peticiones web se deniegan por defecto — la segunda puerta y su valor silencioso por defecto.
- Android Developers — WebSettings.setGeolocationEnabled: la ubicación solo se llama para orígenes seguros en apps destinadas a Android 7.0+ — por qué una página HTTP pierde la ubicación en silencio.
- CISPA — Estudio de los riesgos de privacidad en la ejecución de permisos web de Android WebView (2025): sin consentimiento por sitio, la app puede compartir concesiones del SO con cualquier página — la brecha estructural de privacidad.
- Apple Developer Documentation — WKUIDelegate.requestMediaCapturePermissionFor y WKPermissionDecision — el doble aviso de iOS y la omisión vía delegate.
- web.dev — Web on Android: WebView delega el comportamiento sensible a la app contenedora — por qué el manejo de permisos es del lado de la app, no del web.