Guia de testes ponta a ponta para a página React de liveness hospedada em id-hml.signdocs.com.br (hml) / id.signdocs.com.br (prod).
SigExtLiveness-hml + SigExtCore-hml)mode: PRODUCTION no DynamoDB) — o modo sandbox gera IDs de sessão Rekognition falsos que não funcionam com o componente FaceLivenessDetector do AmplifyTOKEN=$(curl -s -X POST "$API_URL/oauth2/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET&scope=transactions:read transactions:write steps:write" \
| jq -r '.access_token')
TX=$(curl -s -X POST "$API_URL/v1/transactions" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: test-$(date +%s)" \
-d '{
"purpose": "DOCUMENT_SIGNATURE",
"policy": { "profile": "BIOMETRIC" },
"signer": { "name": "Manual Test", "userExternalId": "manual_test_1", "cpf": "12345678901" }
}')
TX_ID=$(echo $TX | jq -r '.transactionId')
STEP_ID=$(echo $TX | jq -r '.steps[0].stepId')
echo "Transaction: $TX_ID, Liveness Step: $STEP_ID"
START=$(curl -s -X POST "$API_URL/v1/transactions/$TX_ID/steps/$STEP_ID/start" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"captureMode": "HOSTED_PAGE"}')
HOSTED_URL=$(echo $START | jq -r '.hostedUrl')
SESSION_ID=$(echo $START | jq -r '.livenessSessionId')
echo "Open in browser: $HOSTED_URL"
$HOSTED_URL no Chrome/Edgecurl -s -X POST "$API_URL/v1/transactions/$TX_ID/steps/$STEP_ID/complete" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"livenessSessionId\": \"$SESSION_ID\"}" | jq .
Esperado: "status": "COMPLETED", "confidence" acima do limiar.
# Obter o ID da etapa de match
MATCH_STEP_ID=$(echo $TX | jq -r '.steps[1].stepId')
# Iniciar match
curl -s -X POST "$API_URL/v1/transactions/$TX_ID/steps/$MATCH_STEP_ID/start" \
-H "Authorization: Bearer $TOKEN" | jq .
# Completar match (o frame de liveness é carregado no servidor a partir do S3)
# O usuário deve ter sido cadastrado previamente (veja etapa 2: userExternalId)
curl -s -X POST "$API_URL/v1/transactions/$TX_ID/steps/$MATCH_STEP_ID/complete" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{}' | jq .
# Upload do documento
curl -s -X POST "$API_URL/v1/transactions/$TX_ID/document" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"content": "'$(echo -n '%PDF-1.4 test' | base64)'"}' | jq .
# Finalizar
curl -s -X POST "$API_URL/v1/transactions/$TX_ID/finalize" \
-H "Authorization: Bearer $TOKEN" | jq .
Em vez de depender de um usuário previamente cadastrado, o banco pode fornecer uma imagem de referência diretamente na requisição de conclusão do match. Isso requer featureFlags.perTransactionReferenceEnabled: true na configuração do tenant no DynamoDB.
TX=$(curl -s -X POST "$API_URL/v1/transactions" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: test-ref-$(date +%s)" \
-d '{
"purpose": "DOCUMENT_SIGNATURE",
"policy": { "profile": "BIOMETRIC" },
"signer": { "name": "Per-Tx Ref Test", "userExternalId": "per_tx_ref_test_1", "cpf": "12345678901" }
}')
TX_ID=$(echo $TX | jq -r '.transactionId')
LIVENESS_STEP_ID=$(echo $TX | jq -r '.steps[0].stepId')
MATCH_STEP_ID=$(echo $TX | jq -r '.steps[1].stepId')
Inicie com HOSTED_PAGE, abra no navegador, capture o rosto e depois complete via API.
# Codifique uma foto conhecida do usuário em base64
REF_IMAGE=$(base64 -w0 /path/to/known-photo.jpg)
# Iniciar match
curl -s -X POST "$API_URL/v1/transactions/$TX_ID/steps/$MATCH_STEP_ID/start" \
-H "Authorization: Bearer $TOKEN" | jq .
# Completar match com referência por transação (frame de liveness carregado no servidor)
curl -s -X POST "$API_URL/v1/transactions/$TX_ID/steps/$MATCH_STEP_ID/complete" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"referenceImage\": {\"source\": \"BASE64_IMAGE\", \"data\": \"$REF_IMAGE\"}}" | jq .
Esperado: "status": "COMPLETED", "similarity" acima do limiar, "enrollmentRef.source": "TRANSACTION_PROVIDED".
Como alternativa ou complemento ao liveness puro, o tipo de etapa DOCUMENT_PHOTO_MATCH
permite comparar a face do usuário com uma foto extraída de um documento (CNH, RG ou passaporte).
Esse fluxo é útil como fallback quando o liveness apresenta dificuldades.
DOCUMENT_PHOTO_MATCH (via CUSTOM)DOCUMENT_PHOTO_MATCH via POST .../steps/{stepId}/startcurl -s -X POST "$API_URL/v1/transactions/$TX_ID/steps/$DOC_STEP_ID/complete" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"documentImage": "'$(base64 -w0 /path/to/cnh-frente.jpg)'",
"documentType": "CNH"
}' | jq .
similarity scoreNota: Quando o tenant possuibiometricRequiredhabilitado, o campogeolocationé obrigatório também paraDOCUMENT_PHOTO_MATCH.
sandbox-*) que o FaceLivenessDetector do Amplify não consegue validar. Você deve usar um tenant em modo PRODUCTION ou atualizar temporariamente a configuração do tenant no DynamoDB (mode: "PRODUCTION").X-Frame-Options: DENY impede a incorporação. A página deve ser aberta em uma nova aba do navegador.Permissions-Policy: camera=(self) restringe o acesso à câmera apenas à mesma origem. A câmera só funcionará ao acessar pelo domínio personalizado.| Cenário | Como Testar | Resultado Esperado |
|---|---|---|
| Fluxo principal (cadastro) | Modo PRODUCTION, câmera real, usuário previamente cadastrado | Liveness aprovado, match carrega o frame no servidor, compara com a imagem de cadastro |
| Fluxo principal (ref. por transação) | Modo PRODUCTION, câmera real, referenceImage no corpo da requisição |
Liveness aprovado, match carrega o frame no servidor, compara com a imagem fornecida, enrollmentRef.source: "TRANSACTION_PROVIDED" |
| Token expirado | Aguarde 10+ minutos após gerar a hostedUrl e então abra-a |
403 do Lambda@Edge |
| Token reutilizado | Abra a hostedUrl 3+ vezes |
1o e 2o carregamento funcionam, 3o carregamento retorna 403 |
| Parâmetros ausentes | Abra https://id-hml.signdocs.com.br sem ?token= |
Estado de erro do React: "Link de verificacao invalido ou expirado" |
| Kill switch (hosted_liveness) | Defina killSwitch.hosted_liveness: true na configuração do tenant (DynamoDB) |
Início da etapa retorna 422 Unprocessable Entity |
| Kill switch (biometric) | Defina killSwitch.biometric: true na configuração do tenant (DynamoDB) |
Início da etapa retorna 422 Unprocessable Entity |
| Sessão inválida | Altere o parâmetro ?session= na URL |
FaceLivenessDetector exibe erro |
| Navegador mobile | Abra a hostedUrl no Safari do iOS / Chrome do Android |
Interface de câmera funciona, captura facial bem-sucedida |
| Sem câmera | Abra em navegador sem câmera | FaceLivenessDetector exibe erro "camera not found" |
| Ambiente | Domínio de Liveness Hospedado | Domínio da API |
|---|---|---|
| hml | id-hml.signdocs.com.br |
api-hml.signdocs.com.br |
| prod | id.signdocs.com.br |
api.signdocs.com.br |
# Verificar se o DNS resolve para o CloudFront
dig id-hml.signdocs.com.br
# Verificar se o HTTPS funciona
curl -I https://id-hml.signdocs.com.br