> ## Documentation Index
> Fetch the complete documentation index at: https://docs.unifokal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Aprovação de ato com passkey

> O módulo passkey faz o titular aprovar um ato seu com a passkey vinculada à conta dele, com evidência conferível sem consultar a UNIFOKAL.

## Aprovação de ato com passkey

O módulo `passkey` faz o titular aprovar um **ato** seu (uma transferência, uma troca de chave Pix, uma alteração de cadastro) com a passkey que ele vinculou à conta dele na sua base. O navegador mostra o pedido do sistema operacional, o titular confirma com a biometria ou o PIN do aparelho, e a assinatura cobre o **resumo do ato** que você enviou na criação da sessão. Não há selfie nem documento nesse caminho: quem responde é a chave.

**O vínculo nasce de prova, nunca de pedido.** Você liga o vínculo no flow de onboarding (`passkey_bind`), o titular cria a chave durante a sessão, e ela só passa a valer quando a verificação aprova pelo caminho automático. Com rosto e prova de vida aprovados, a chave nasce com garantia de `identity`; com prova de vida só, nasce com garantia de `presence`. Aprovação manual na fila de revisão não vincula chave nenhuma, e a sessão de aprovação de ato também não pode ser aprovada nem reprocessada pelo painel. O desfecho do vínculo chega no próprio `verification.completed`, em `data.passkey_binding`: vinculou, com o id da chave, ou não vinculou, com o motivo. Se a janela da confirmação não abrir, o titular pode tentar de novo ou seguir sem a passkey: a verificação continua e o vínculo sai como `not_registered`.

**O ato vai na criação da sessão, no campo `act`.** O backend canoniza o ato, calcula o `act_digest` (SHA-256 da forma canônica) e guarda o resumo cifrado para mostrar ao titular. O desafio que o aparelho assina é derivado desse digest, então a assinatura não serve para outro ato. A sessão de ato vale 10 minutos, e o titular pode recusar o ato na própria tela, o que gera o evento `act.rejected`.

```
// passkey no check_details: o ato foi aprovado pela chave vinculada
{ "module": "passkey", "passed": true, "outcome": "approved", "score": 100,
  "data": { "passkey_id": "spk_01J9ZC6Q8R3M2V7K4T5N1B0XHD",
            "bound_by": "registration",          // registration | presence | existing_key | reject
            "assurance": "identity",       // identity | presence
            "backup_eligible": false, "backup_state": false,
            "user_verified": true,
            "act_digest": "b64url-do-sha256-do-ato-canonico",
            "act_kind": "pix_transfer",
            "evidence": { "format": "unifokal/passkey-assertion@1", "...": "..." },
            "model_version": "passkey-v1" } }

// passkey: a assinatura foi conferida e reprovou (veredito sobre a credencial)
{ "module": "passkey", "passed": false, "outcome": "failed", "score": 0,
  "data": { "passkey_id": "spk_01J9ZC6Q8R3M2V7K4T5N1B0XHD", "bound_by": "registration",
            "assurance": "identity", "backup_eligible": null, "backup_state": null,
            "user_verified": null, "act_digest": null, "act_kind": null, "evidence": null,
            "reason": "passkey_assertion_invalid",
            "model_version": "passkey-v1" } }

// passkey no sandbox: a cerimônia pode ser simulada, e o bloco diz quando foi
{ "module": "passkey", "passed": true, "outcome": "approved", "score": 100,
  "data": { "passkey_id": "spk_01SANDBOXEXEMPLO0000000000", "bound_by": "registration",
            "assurance": "identity", "backup_eligible": true, "backup_state": true,
            "user_verified": true, "act_digest": "b64url-do-sha256-do-ato-canonico",
            "act_kind": "pagamento", "simulated": true,
            "evidence": { "format": "unifokal/passkey-assertion@1", "simulated": true, "...": "..." },
            "model_version": "passkey-v1" } }
```

Os motivos em `data.reason`: `passkey_assertion_invalid` (a assinatura não confere), `passkey_backup_flag_changed` (a chave mudou de estado de cópia desde o vínculo), `passkey_not_device_bound` (a política pedia chave presa ao aparelho), `passkey_counter_regressed` (o contador do autenticador voltou, vai para revisão) e `passkey_not_completed` (o titular não concluiu, sem cobrança).

**A evidência é conferível sem nos consultar.** O bloco `evidence` traz o RP ID, a origem, a chave pública em JWK, os dados do autenticador, o `client_data_json`, a assinatura, o nonce e o ato canônico. O desafio assinado é `SHA-256("unifokal/passkey-act/v1" || nonce || act_digest)`, e a assinatura cobre `authenticator_data || SHA-256(client_data_json)`, como em qualquer asserção WebAuthn. Guarde a evidência junto do ato: ela prova, anos depois, o que o titular aprovou.

Os eventos de webhook do módulo são `passkey.bound` (a chave passou a valer), `passkey.revoked` (revogada pelo painel ou pelo apagamento do titular) e `act.rejected` (o titular recusou o ato). A revogação pelo painel exige a confirmação do segundo fator de quem opera.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.