Skip to main content
Não existe SDK nativo para iOS ou Android hoje — quando existir, esta página aponta para ele. Até lá, o app nativo abre a verificação dentro de uma WebView, e tem dois caminhos:
  • Carregar a mesma página que você usaria num navegador, com o Web SDK montado pelo CDN ou com o iframe próprio. Nada nessa página muda por rodar numa WebView.
  • Carregar o embed direto e receber os eventos em código nativo, pela ponte descrita abaixo. Sem iframe, sem Web SDK, sem JavaScript seu.
Nos dois casos, o que muda em relação ao navegador é a permissão que o seu app precisa conceder para a câmera, o microfone e a geolocalização funcionarem dentro da WebView. Ao final deste guia, a verificação abre dentro da WebView do seu app, a captura de documento, a prova de vida e a coleta de geolocalização têm acesso ao que cada uma precisa, e o desfecho chega ao seu código nativo.

A ponte nativa

O app injeta window.LegitimuzNative na página antes de o embed rodar. A presença do objeto é o próprio sinal de confiança: diferente do iframe, não há handshake de origem nem buffer. Detectado o objeto, o embed para de usar postMessage de iframe e passa a falar com o seu host. O contrato que o seu app precisa expor:

O que chega no seu postMessage

Cada mensagem é uma string JSON com o mesmo envelope:
Descarte qualquer mensagem cujo source não seja legitimuz ou cuja version não seja 1.
Na ponte nativa o payload de cancelamento traz session_id, em snake_case. O Web SDK entrega sessionId para código web; nativo recebe o nome do protocolo, sem tradução.

O que você manda de volta

Um comando, hoje:
Chame ao desmontar a tela. É o que encerra a sessão do embed do lado da página — o equivalente nativo do handle.destroy() do Web SDK.

Montar a WKWebView

O essencial da configuração, em Swift:
embedURL é a sdkUrl que o seu backend recebeu de entry.url — veja criar verificações.
O embed não abre janela: window.open é inócuo dentro do iframe e da WebView. Implemente webView(_:createWebViewWith:for:windowFeatures:) e trate navigationAction.targetFrame == nil em decidePolicyFor para abrir esses endereços — termos de uso, por exemplo — no navegador do sistema. Sem isso o toque do titular não faz nada e ele trava na tela.
Ao desmontar, execute o destroy e remova o handler, nesta ordem:

Por que a permissão não vem sozinha

Um navegador pede câmera, microfone e localização ao usuário quando a página chama getUserMedia ou navigator.geolocation. Uma WebView embutida no seu app não pede: por padrão, ela nega em silêncio, mesmo a página declarando a permissão corretamente — o que o Web SDK já faz no iframe que ele cria (allow="camera; microphone; geolocation; fullscreen; accelerometer; gyroscope", ver segurança). Sem o passo nativo abaixo, a captura e a coleta de localização falham sem nenhum erro que a sua página consiga tratar.

iOS — WKWebView

Declare os três textos de uso no Info.plist e implemente o delegado que autoriza câmera e microfone:
Info.plist
requestMediaCapturePermissionFor existe a partir do iOS 15 e cobre câmera e microfone. Sem o delegado, a WKWebView recusa a concessão por padrão, mesmo com as chaves do Info.plist presentes. getUserMedia em si só funciona em WKWebView de app de terceiro a partir do iOS 14.3; abaixo disso a API não existe na página.
Geolocalização não passa por delegado nenhum: a WKWebView não tem um método equivalente ao requestMediaCapturePermissionFor para navigator.geolocation hoje. Quem pede a autorização de localização ao sistema é o próprio WebKit — seu app não chama nada —, mas só faz isso com a chave NSLocationWhenInUseUsageDescription presente no Info.plist. Sem ela, o pedido nunca é feito e a página trava esperando uma posição que nunca chega, sem erro nenhum. Passe um timeout explícito em getCurrentPosition/watchPosition para não depender de detectar isso pelo travamento.

Android — WebView

Declare as permissões no AndroidManifest.xml, peça a permissão em tempo de execução ao usuário (Android 6.0+ exige isso independentemente da WebView) e conceda no WebChromeClient. Câmera e microfone passam por onPermissionRequest; geolocalização é um método separado, onGeolocationPermissionsShowPrompt, e precisa da própria configuração ligada no WebSettings:
AndroidManifest.xml
Conceder aqui sem a permissão de runtime concedida ao app não libera nada: são duas camadas separadas, e as duas precisam estar de acordo. E sem setGeolocationEnabled(true), onGeolocationPermissionsShowPrompt nunca dispara.

Framework híbrido (React Native, Flutter e afins)

O mecanismo por baixo é o mesmo dos dois itens acima — o wrapper só decide como ele expõe isso. Confira na documentação da versão do wrapper que você usa como conceder a permissão de mídia da WebView nos dois sistemas; nem toda versão cobre iOS e Android da mesma forma. No iOS, getUserMedia não funciona quando a página é servida por um esquema de URL customizado em vez de https:// ou http://localhost — é limitação da própria WKWebView, não do wrapper. Confirme qual esquema a versão do seu wrapper usa antes de depurar a permissão.
A falha muda por plataforma e por capacidade, e vale saber qual é qual antes de abrir um chamado:
  • Android, câmera sem onPermissionRequest: erro imediato (NotAllowedError) — o default do WebChromeClient é negar.
  • Android, geolocalização sem onGeolocationPermissionsShowPrompt: nenhum erro. O callback nunca é chamado, e a chamada trava até o timeout que a página passar — ou para sempre, se ela não passar nenhum.
  • iOS, câmera sem o delegado: a WKWebView mostra o prompt do próprio sistema e funciona sem código nenhum, contanto que a chave do Info.plist esteja presente.
  • iOS, geolocalização sem a chave do Info.plist: o mesmo travamento silencioso do Android, por um motivo diferente (veja o aviso acima).
Nas duas plataformas, “passo nativo ausente” e “titular recusou a permissão” chegam à página como o mesmo erro na maioria dos casos. Confirme o lado nativo antes de tratar a ausência de coleta como recusa do titular.

Iframe próprio

Se a página que a WebView abre não carrega o Web SDK, veja o que muda no tratamento de eventos.