Captura de selfie com prova de vida — sessão no backend, captura no SDK, JWT assinado pela Unico.
Biometrics SDK (Web)
O Biometrics SDK faz a captura de uma selfie com prova de vida
(liveness) dentro da sua aplicação web, sem que o dado biométrico passe
pelo seu código.
O fluxo tem duas pontas suas:
- O seu backend cria a sessão na API da Autra com as credenciais OAuth2
que você já tem. - O seu frontend entrega essa sessão ao SDK, que conduz a captura e
resolve com um JWT assinado pela Unico — a prova de que a captura veio
de uma selfie legítima.
A imagem da selfie nunca chega ao seu código. O SDK não expõe, nãoarmazena e não transmite dado biométrico para a sua aplicação; ele entrega
apenas o JWT resultante.
O escopo do produto é captura, não verificação. A Autra entrega a você o
JWT; a decisão sobre esse JWT acontece no seu backend.
Antes de começar
A biometria é habilitada e configurada pelo time da Autra. Nenhum item
desta seção é auto-serviço: você abre uma solicitação, a Autra aplica e
confirma, e só então a integração funciona. Trate isso como um passo de
projeto, não como um detalhe de última hora.
Enquanto a liberação não acontece, POST /v1/biometrics/sessions responde
403 BIOMETRICS_FEATURE_DISABLED e o SDK nunca chega a abrir a captura.
1. O que solicitar à Autra
| Solicitação | Por que é necessária | O que informar |
|---|---|---|
| Habilitar a biometria no seu tenant | Sem ela, a criação de sessão é recusada com 403 BIOMETRICS_FEATURE_DISABLED. | Tenant e em quais ambientes (sandbox, production ou ambos). |
| Cadastrar as origens (domínios) do seu app | Sem origem cadastrada, a captura embutida (modal) fica indisponível e o SDK só consegue operar em redirect. | Um domínio por ambiente, no formato https://host[:porta]. Veja Origens autorizadas. |
| Liberar os IPs do seu backend | POST /v1/biometrics/sessions passa pela allowlist de IP do tenant, como os demais endpoints server-to-server. | IPs ou faixas de saída do seu backend, por ambiente. |
Credenciais OAuth2 (client_id / client_secret) | Autenticam as chamadas do seu backend. | Só se você ainda não tiver as credenciais usadas nas demais APIs da Autra. Veja Generate access token. |
| Ajustar TTL e tentativas (opcional) | Os padrões (10 minutos, 5 tentativas) atendem a maioria dos casos. | Os valores desejados, dentro das faixas em Configuração da sua conta. |
Modelo de solicitação:
Assunto: Liberação de biometria — <sua empresa>
Tenant.......: <nome ou ID do tenant>
Ambientes....: sandbox e production
Origens......: sandbox → https://app-hml.seudominio.com.br
production → https://app.seudominio.com.br
IPs do backend: production → 200.0.0.10, 200.0.0.11
sandbox → 200.0.0.50
TTL/tentativas: manter o padrão (600s / 5 tentativas)
Mudou de domínio, subiu um subdomínio novo ou trocou o ambiente de
homologação? Solicite o cadastro da nova origem antes do deploy. Aautorização é fail-closed: uma origem não cadastrada não gera erro de
configuração no build — ela simplesmente remove a captura embutida do
conjunto de modos disponíveis em produção.
2. O que você prepara do seu lado
| Item | Detalhe |
|---|---|
| Endpoint no seu backend que cria a sessão | Chama POST /v1/biometrics/sessions e devolve a sessão ao seu frontend. Nunca exponha o client_secret ao browser. |
| Endpoint no seu backend que recebe o JWT | Recebe o resultado da captura logo após o desfecho. |
| Páginas servidas em HTTPS | O SDK recusa URLs que não sejam HTTPS. |
Tela de retorno (só no modo redirect) | A página do returnUrl que lê o resultado. Veja Tratando o retorno no modo redirect. |
| Navegador moderno com câmera | Desktop ou mobile. |
Como funciona
Seu backend Seu app web + SDK Autra
| | |
| POST /v1/biometrics/sessions |
|-----------------------------------------------------------> |
| sessionId, sessionToken, captureUrl, embedModes |
| <-----------------------------------------------------------|
| | |
|--- repassa a sessão -------->| |
| | session.open() |
| |------------------------------>|
| |<---------- ready -------------|
| |<---- camera_requested --------|
| |<---- capture_started ---------|
| |<---------- jwt ---------------|
|<-- POST /api/kyc { jwt } ----| |
| | |
| GET /v1/biometrics/sessions/{id} (confirmação server-side) |
|-----------------------------------------------------------> |Passo 1 — Backend: criar a sessão
POST /v1/biometrics/sessions
POST /v1/biometrics/sessionsPOST /v1/biometrics/sessions
Host: api.autra.io
Authorization: Bearer <access_token OAuth2>
Content-Type: application/json
{
"referenceId": "customer-42",
"flow": "selfie",
"environment": "production"
}Corpo da requisição
| Campo | Tipo | Obrigatório | Notas |
|---|---|---|---|
environment | "production" | "sandbox" | ✓ | Qualquer outro valor → 400. |
flow | string | — | Padrão "selfie". Na v1 é o único valor aceito; outro valor → 400. |
referenceId | string | — | Seu identificador do usuário/caso. Volta em GET /sessions/{id} para conciliação. |
Resposta 201
{
"sessionId": "0f6c2f2c-6b6b-4f0a-9c5a-2f8f3b6c1d44",
"sessionToken": "…",
"hostedPageUrl": "https://biometria.autra.io/sessions/0f6c2f2c-6b6b-4f0a-9c5a-2f8f3b6c1d44",
"expiresAt": "2026-08-24T18:32:00Z",
"maxAttempts": 5,
"embedModes": ["iframe", "popup", "redirect"]
}| Campo | Descrição |
|---|---|
sessionId | UUID opaco da sessão. |
sessionToken | Entregue uma única vez — a Autra guarda apenas o hash. Não há como recuperá-lo depois. Nunca registre em log. |
hostedPageUrl | Endereço opaco da captura desta sessão. Repasse ao SDK sem interpretar, sem reescrever e sem armazenar: o formato é interno e pode mudar sem aviso. Já vem sem o token. |
expiresAt | Fim do TTL da sessão (UTC, RFC 3339). |
maxAttempts | Tentativas de captura permitidas nesta sessão. |
embedModes | Modos de captura autorizados para este tenant/ambiente. Repasse sem traduzir ao SDK. |
sessionTokené uma credencial de vida curta. Entregue-a ao seufrontend pela sua própria API, no corpo da resposta — nunca em query
string, nunca em log, nunca persistida.
embedModes é fail-closed. Se o seu tenant não tiver nenhuma origem
ativa cadastrada para aquele environment, a resposta vem sem iframe
(["popup", "redirect"]) e o SDK não tenta a captura embutida. O valor
popup ainda é devolvido por compatibilidade; o SDK aceita e ignora.
Erros
| HTTP | Código | Quando |
|---|---|---|
| 400 | — | JSON inválido, environment ausente/inválido, flow não suportado. |
| 403 | BIOMETRICS_FEATURE_DISABLED | Biometria não habilitada para o tenant — solicite a liberação à Autra. |
| 403 | — | Token sem tenant válido / IP fora da allowlist. |
| 500 | — | Falha interna. |
Passo 2 — Frontend: conduzir a captura
Instalação
npm install @autra-io/biometrics-sdk-web- ESM + CJS +
.d.ts, tree-shakeable (sideEffects: false) - Sem dependência de runtime, agnóstico de framework (sem React)
- Bundle ESM ≤ 15 KB minificado + gzip (verificado no CI)
Uso
import { AutraBiometrics, AutraBiometricsError } from '@autra-io/biometrics-sdk-web';
// 1. o seu backend cria a sessão e devolve estes campos
const { sessionId, sessionToken, hostedPageUrl, embedModes } =
await fetch('/api/biometrics/session', { method: 'POST' }).then((r) => r.json());
// 2. instancie o SDK (nenhuma chamada de rede acontece aqui)
const biometrics = AutraBiometrics.create({
environment: 'production', // "production" | "sandbox"
locale: 'pt-BR', // opcional
debug: false, // opcional — nunca loga sessionToken nem jwt
});
const session = biometrics.createSession({
sessionId,
sessionToken,
hostedPageUrl, // repasse direto da resposta do backend
embedModes, // idem
preferredMode: 'auto', // "auto" | "modal" | "redirect"
timeoutMs: 300_000,
});
session.on('ready', () => console.log('captura pronta'));
session.on('mode_selected', ({ mode }) => console.log('modo', mode));
session.on('camera_requested', () => console.log('pedindo câmera'));
session.on('capture_started', ({ attempt }) => console.log('tentativa', attempt));
session.on('capture_retry', ({ attempt, code }) => console.log('retry', attempt, code));
try {
const result = await session.open();
// { sessionId, jwt, attempts, capturedAt }
// 3. envie o JWT ao SEU backend imediatamente
await fetch('/api/kyc', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ sessionId: result.sessionId, jwt: result.jwt }),
});
} catch (err) {
if (err instanceof AutraBiometricsError) {
if (err.code === 'cancelled_by_user') return; // usuário fechou, não é erro
showError(err.code, err.retryable);
}
} finally {
session.destroy();
}
Ojwté uma credencial de TTL curto (minutos). Envie ao seu backendimediatamente, não persista e não registre em log.
Passo 3 — Backend: confirmar o resultado
Nunca trate o resultado do browser como a fonte de verdade sozinho. Confirme
o estado da sessão pela API antes de liberar qualquer coisa de valor.
GET /v1/biometrics/sessions/{sessionId}
GET /v1/biometrics/sessions/{sessionId}GET /v1/biometrics/sessions/0f6c2f2c-6b6b-4f0a-9c5a-2f8f3b6c1d44
Authorization: Bearer <access_token OAuth2>{
"sessionId": "0f6c2f2c-6b6b-4f0a-9c5a-2f8f3b6c1d44",
"status": "CAPTURED",
"referenceId": "customer-42",
"attempts": 2,
"maxAttempts": 5,
"createdAt": "2026-08-24T18:22:00Z",
"expiresAt": "2026-08-24T18:32:00Z",
"terminatedAt": "2026-08-24T18:24:11Z",
"lastFailureCode": null
}A sessão é sempre escopada ao tenant do token: uma sessão de outro tenant
responde 404, nunca 403 — a API não revela a existência do recurso.
POST /v1/biometrics/sessions/{sessionId}/cancel
POST /v1/biometrics/sessions/{sessionId}/cancel{ "status": "CANCELLED" }Idempotente: cancelar uma sessão já terminada devolve 200 com o
estado atual. O 409 BIOMETRICS_SESSION_CONFLICT fica reservado para
corrida real (o estado mudou entre a leitura e a escrita).
Ciclo de vida da sessão
| Estado | Significado |
|---|---|
CREATED | Sessão criada; o SDK ainda não abriu a captura. |
AUTHORIZED | O SDK abriu a captura e o sessionToken foi validado. |
CAPTURING | Uma tentativa de captura está em andamento. |
CAPTURED | ✅ Terminal — captura concluída, JWT emitido. |
FAILED | ❌ Terminal — tentativas esgotadas ou falha definitiva. |
CANCELLED | ❌ Terminal — cancelada pelo usuário ou pelo seu backend. |
EXPIRED | ❌ Terminal — TTL vencido. |
Regras:
- Estado terminal é absorvente: uma sessão terminada nunca volta atrás.
Para uma nova tentativa, crie uma nova sessão. - Recarregar a página não consome tentativa — a autorização é
idempotente, inclusive quando a aba volta do segundo plano no meio da
captura (CAPTURING → AUTHORIZED). maxAttemptse o TTL são congelados na criação da sessão: um ajuste
aplicado pela Autra depois vale só para as sessões criadas dali em diante.
Modos de captura
| Modo | O que o usuário vê | Quando usar |
|---|---|---|
modal (iframe no vocabulário da API) | A captura abre em um overlay sobre a sua página; o usuário nunca sai do seu app. | Modo primário. Exige a origem do seu app cadastrada. |
redirect | O navegador leva o usuário para a captura e volta ao seu returnUrl com o resultado. | Quando o seu app não pode embutir conteúdo externo (CSP, webview, política interna). |
preferredMode: 'auto' (padrão) tenta modal e cai para redirect
apenas se você passou returnUrl. Um modo explícito ('modal' ou
'redirect') desliga o fallback automático.
Se você passar embedModes vindo do backend, a sequência resolvida é
filtrada por ele — mesmo que contrarie o preferredMode. É o comportamento
fail-closed: sem iframe autorizado, o modal nunca é tentado.
Tratando o retorno no modo redirect
No modo redirect, open() não resolve — o navegador sai da sua página. O
resultado chega na página do returnUrl, no fragmento da URL (fragmento, e
não query string: assim o valor não vaza para logs de servidor nem para o
header Referer). Leia-o com readRedirectResult():
import { readRedirectResult } from '@autra-io/biometrics-sdk-web';
const outcome = readRedirectResult(); // lê e limpa o fragmento de window.location
if (outcome?.type === 'result') {
await fetch('/api/kyc', {
method: 'POST',
body: JSON.stringify({ jwt: outcome.result.jwt }),
});
} else if (outcome?.type === 'error') {
showError(outcome.error.code);
}
// undefined = carregamento normal da página, não um retorno de capturareadRedirectResult(href) aceita uma URL explícita para SSR/testes; nesse
caso window.location não é tocado. A função nunca lança — um fragmento
malformado vira { type: 'error', error: message_invalid }.
Referência do SDK
AutraBiometrics.create(options)
AutraBiometrics.create(options)| Opção | Tipo | Obrigatório | Notas |
|---|---|---|---|
environment | "production" | "sandbox" | ✓ | Outro valor → configuration_error. |
locale | string | — | Idioma da captura. Valor não suportado cai para pt-BR, sem erro. |
debug | boolean | — | Loga apenas transições de estado. Nunca loga sessionToken nem jwt. |
Não faz nenhuma chamada de rede.
biometrics.createSession(options)
biometrics.createSession(options)| Opção | Tipo | Obrigatório | Notas |
|---|---|---|---|
sessionId | string | ✓ | Vindo do seu backend. |
sessionToken | string | ✓ | Vindo do seu backend. Nunca logado, nunca persistido. |
hostedPageUrl | string | ✓ | Vindo do seu backend, repassado sem alteração. Precisa ser HTTPS absoluto. |
embedModes | Array<"modal" | "iframe" | "popup" | "redirect"> | — | Repasse direto de embedModes da API. Omitir = não filtrar. |
preferredMode | "auto" | "modal" | "redirect" | — | Padrão "auto". |
returnUrl | string | obrigatório se preferredMode: "redirect" | Precisa ser HTTPS. |
timeoutMs | number | — | Teto local de UI; padrão 300000. O servidor continua sendo a autoridade sobre o TTL. |
Não faz nenhuma chamada de rede. Entrada inválida lança
AutraBiometricsError com código configuration_error de forma
síncrona.
session.open(): Promise<AutraBiometricsResult>
session.open(): Promise<AutraBiometricsResult>type AutraBiometricsResult = {
sessionId: string;
jwt: string; // credencial de TTL curto — envie ao backend, não persista, não logue
attempts: number;
capturedAt: string; // RFC 3339
};- Chamar
open()duas vezes na mesma sessão rejeita a segunda com
invalid_session. - Apenas uma captura embutida por instância de
AutraBiometricspode
estar aberta por vez; a segunda sessão que tentar rejeita com
configuration_error.
session.on(event, listener): Unsubscribe
session.on(event, listener): Unsubscribe| Evento | Payload | Quando |
|---|---|---|
ready | — | A captura terminou de carregar e está pronta. |
mode_selected | { mode: "modal" | "redirect" } | O SDK escolheu o modo de captura. |
camera_requested | — | O SDK pediu permissão de câmera ao usuário. |
capture_started | { attempt: number } | Início de uma tentativa. |
capture_retry | { attempt: number; code: string } | Tentativa falhou e haverá nova. |
closed | — | A captura foi fechada. |
O retorno de on() é a função de unsubscribe.
session.close() / session.destroy()
session.close() / session.destroy()close()fecha a captura ativa; oopen()pendente rejeita com
cancelled_by_host.destroy()remove listeners, a UI e os timers. É idempotente e seguro
chamar múltiplas vezes — chame sempre nofinally.
AUTRA_BIOMETRICS_SDK_VERSION
AUTRA_BIOMETRICS_SDK_VERSIONConstante com a versão publicada do pacote, injetada em tempo de build.
Códigos de erro
Toda falha do SDK chega como AutraBiometricsError:
class AutraBiometricsError extends Error {
readonly code: AutraBiometricsErrorCode;
readonly sessionId?: string;
readonly retryable: boolean;
}| Código | retryable | Significado |
|---|---|---|
configuration_error | não | Parâmetro inválido, URL não-HTTPS, ou outra captura já aberta. |
invalid_session | não | open() chamado duas vezes, ou sessão já destruída. |
session_expired | não | TTL da sessão venceu. |
session_already_used | não | Sessão já terminada. |
origin_not_allowed | não | A origem do seu app não está cadastrada para este ambiente — solicite o cadastro à Autra. |
feature_disabled | não | Biometria não habilitada para o tenant. |
camera_permission_denied | não | Usuário negou a câmera. |
camera_unavailable | sim | Sem câmera disponível ou em uso por outro app. |
browser_unsupported | não | Navegador sem as APIs necessárias, ou execução fora do browser (SSR). |
capture_failed | sim | A captura falhou (liveness não aprovado, imagem inválida). |
attempts_exhausted | não | maxAttempts consumidas. |
cancelled_by_user | não | Usuário fechou a captura. Trate como fluxo normal, não como erro. |
cancelled_by_host | não | close()/destroy() chamados pela sua aplicação. |
timeout | sim | Estourou o timeoutMs local. |
network_error | sim | Falha de rede ao carregar a captura. |
message_origin_rejected | não | O SDK recebeu uma mensagem de origem inesperada e a descartou. |
message_invalid | não | Mensagem malformada ou fragmento de retorno corrompido. |
retryable indica se repetir a captura faz sentido — na prática, criar
uma nova sessão e abrir de novo.
Configuração da sua conta
Os parâmetros abaixo pertencem ao seu tenant e são aplicados pelo time da
Autra. Não há endpoint público nem tela de auto-serviço para eles: para
criar, alterar ou remover qualquer um, abra uma solicitação com a Autra. Toda
alteração é auditada.
Parâmetros da sessão
| Parâmetro | Padrão | Faixa aceita | Efeito |
|---|---|---|---|
| TTL da sessão | 600 segundos (10 min) | 60 – 600 | Prazo entre a criação da sessão e o desfecho da captura. Vencido, a sessão vai para EXPIRED. |
| Tentativas por sessão | 5 | 1 – 6 | Quantas capturas o usuário pode tentar antes de a sessão ir para FAILED. |
Os dois valores são congelados na criação de cada sessão: uma alteração
combinada com a Autra passa a valer para as sessões criadas dali em diante,
nunca para as que já estão em andamento.
Origens autorizadas
Necessárias para o modo modal: a Autra só autoriza a captura embutida em
origens previamente cadastradas.
Formato da origem: https://host[:porta] — sem caminho, sem barra final,
sem http://.
✅ https://app.seudominio.com.br
✅ https://checkout.seudominio.com.br:8443
❌ https://app.seudominio.com.br/
❌ https://app.seudominio.com.br/checkout
❌ http://app.seudominio.com.brAs origens são por ambiente: cadastrar em sandbox não libera production —
informe as duas listas na solicitação.
Para conferir o que está ativo hoje, olhe o embedModes da resposta de
POST /v1/biometrics/sessions: a presença de iframe confirma que a origem
daquele ambiente está cadastrada. Se iframe não aparecer, é sinal de origem
faltando — solicite o cadastro à Autra.
Segurança
- O SDK nunca recebe uma credencial da API da Autra. Ele recebe apenas o
sessionTokende vida curta emitido pelo seu backend. - O
sessionTokené entregue uma única vez e guardado apenas como hash,
comparado em tempo constante. Não é recuperável. - O token nunca trafega em URL que o seu servidor possa registrar: a API
devolvehostedPageUrllimpa e o SDK acopla o token no browser, em um
ponto da URL que não chega ao servidor nem ao headerReferer. - Canal de mensagens endurecido: o SDK valida a origem e a janela de
cada mensagem que recebe, usa envelope versionado e ignora qualquer
mensagem após o desfecho da sessão. - Todas as URLs precisam ser HTTPS (
hostedPageUrl,returnUrl). jwté credencial: envie ao seu backend imediatamente, nunca persista,
nunca logue.- Não confie apenas no cliente: confirme o desfecho com
GET /v1/biometrics/sessions/{sessionId}antes de liberar qualquer coisa
de valor.
Solução de problemas
| Sintoma | Causa provável |
|---|---|
configuration_error em createSession() | hostedPageUrl/returnUrl não é HTTPS ou não é URL absoluta; falta campo obrigatório; preferredMode: "redirect" sem returnUrl. |
configuration_error em open() | Outra sessão da mesma instância já tem uma captura aberta. |
| A captura nunca abre em modal | embedModes veio sem iframe — a origem do seu app não está cadastrada para esse ambiente. Solicite o cadastro à Autra. |
open() nunca resolve no modo redirect | Esperado: o navegador sai da sua página. Trate o resultado no returnUrl com readRedirectResult(). |
browser_unsupported | Chamado fora do browser (SSR) ou sem as APIs de DOM/câmera que o modo exige. |
camera_permission_denied mesmo com o usuário aceitando | A sua página está dentro de outro iframe ou tem Permissions-Policy restritiva, bloqueando o acesso à câmera. |
message_origin_rejected no console | O SDK descartou uma mensagem de origem inesperada — não vem da captura legítima. |
403 BIOMETRICS_FEATURE_DISABLED | Biometria não habilitada para o tenant. Solicite a liberação à Autra. |
404 em GET /sessions/{id} | A sessão não existe ou pertence a outro tenant. |
409 BIOMETRICS_SESSION_CONFLICT no cancel | O estado mudou concorrentemente. Releia com GET /sessions/{id}. |
Referência rápida de endpoints
| Método | Rota | Auth | Uso |
|---|---|---|---|
POST | /v1/biometrics/sessions | OAuth2 Bearer + IP allowlist | Cria a sessão |
GET | /v1/biometrics/sessions/{sessionId} | OAuth2 Bearer | Consulta o estado |
POST | /v1/biometrics/sessions/{sessionId}/cancel | OAuth2 Bearer | Cancela (idempotente) |
São os três endpoints da integração — não há endpoint de configuração: TTL,
tentativas, origens autorizadas e habilitação da feature são aplicados pela
Autra mediante solicitação. Veja Configuração da sua
conta.
Licença
@autra-io/biometrics-sdk-web é distribuído sob Apache-2.0.
