Como funciona
Esta página acompanha uma assinatura pelo CloudHub e explica o que você escolhe no caminho: o tipo de sessão, como o CloudHub descobre os provedores, quais URIs de redirecionamento ele aceita e como levar os seus próprios dados até o retorno do usuário. Primeiros passos transforma tudo isso em código.
Provedores e certificados em nuvem
Um provedor (PSC, Prestador de Serviço de Confiança) guarda certificados e as chaves privadas deles em um HSM e permite que o titular os use remotamente. A interface dele segue o DOC-ICP-17.01 do ITI, que é OAuth 2.0: a aplicação envia o usuário à página de autorização do provedor, o usuário entra e aprova, e a aplicação recebe um token que permite assinar com a chave desse usuário.
Uma pessoa pode ter certificados em vários provedores, e mais de um certificado no mesmo provedor. O CloudHub esconde o protocolo, mas não essa escolha: quem decide o provedor é o usuário.
O fluxo, passo a passo
1. Criar uma sessão
O seu backend chama POST /api/sessions com o CPF (ou CNPJ) do signatário, a URI de
redirecionamento para onde o CloudHub deve devolver o usuário e o tipo de sessão.
O CloudHub pergunta a cada provedor se ele tem um certificado para esse identificador e responde com
uma entrada para cada provedor que tem:
{
"services": [
{
"serviceInfo": {
"serviceName": "BirdID",
"provider": "SOLUTI",
"endpoint": "https://api.birdid.com.br/",
"badgeUrl": "https://cdn.lacunasoftware.com/img/psc/birdid.png"
},
"authUrl": "https://api.birdid.com.br/oauth/authorize?client_id=…"
}
]
}
Nada foi autorizado ainda. Uma lista services vazia significa que nenhum provedor tem um
certificado para esse identificador, ou que nenhum dos que têm respondeu a tempo.
2. Enviar o usuário a um provedor
Mostre os provedores, por exemplo com badgeUrl como logotipo e provider como legenda, e leve o
navegador para a authUrl da opção escolhida. O usuário entra no provedor e aprova a solicitação,
normalmente no aplicativo móvel do provedor, com um PIN ou lendo um QR code.
3. O usuário volta com uma sessão
O provedor redireciona o navegador para o CloudHub, o CloudHub troca o código de autorização pelo
token do provedor e o guarda, e então redireciona o navegador para a sua URI de redirecionamento com
o parâmetro session acrescentado. Os parâmetros que você colocou na URI de redirecionamento são
mantidos:
https://app.example.com/sign/complete?fileId=42&session=9f2c…e81a
Se o usuário cancelar, se o provedor recusar ou se a troca falhar, o CloudHub também redireciona
para a sua URI de redirecionamento, mas sem o parâmetro session. O CloudHub não informa o
motivo. Trate a falta de session como "não autorizado" e deixe o usuário tentar de novo.
4. Ler o certificado
Com a sessão, GET /api/sessions/certificate devolve o certificado do signatário, em base64.
GET /api/v2/sessions/certificate devolve o mesmo certificado junto com o alias dele e o
serviceName do provedor.
Quando o provedor tem mais de um certificado para o usuário, o CloudHub escolhe um: um certificado
dentro da validade tem preferência sobre um que não esteja e, entre eles, vale aquele cuja validade
começou mais recentemente. Para garantir que o hash seja assinado exatamente com o certificado que
você leu, passe o alias dele na próxima chamada.
5. Assinar o hash
A sua biblioteca de assinatura calcula o hash a assinar a partir do documento e do certificado. O
seu backend o envia para POST /api/sessions/sign-hash com a sessão e o algoritmo de hash, e o
CloudHub devolve o valor da assinatura, em base64. A biblioteca de assinatura então insere a
assinatura e o certificado no documento assinado.
Sessões
O valor da sessão
O valor session que a sua URI de redirecionamento recebe é uma string hexadecimal de 64
caracteres. O CloudHub guarda o token do provedor; a sua aplicação guarda só esse valor e o envia em
toda chamada que usa a sessão.
Sem a sua chave de API, uma sessão não serve para nada, mas, juntas, elas permitem assinar com a chave do usuário. Por isso, trate o valor da sessão como uma credencial: use-o logo, mantenha-o fora dos seus logs e não o guarde por mais tempo do que precisa. Ele chega em uma query string, então pode parar no histórico do navegador e no log de acesso do seu servidor web; use HTTPS e configure os logs de acesso de acordo.
Tipos de sessão
O tipo de sessão, enviado em type ao criar a sessão, diz o que o usuário autoriza.
| Tipo | O usuário autoriza | Use para |
|---|---|---|
SingleSignature | Uma assinatura. É o padrão. | Assinar um documento. Assinar um desafio para autenticar o usuário. |
SignatureSession | Qualquer número de assinaturas até a sessão expirar. | Assinar vários documentos com uma única autorização. |
MultiSignature | Uma operação de assinatura que cobre um ou mais hashes. | Depende do provedor. O CloudHub assina um hash por chamada a sign-hash, então, para vários documentos, prefira SignatureSession. |
AuthenticationSession | Nenhuma assinatura: só a leitura do certificado. | Não recomendado. A maioria dos provedores não oferece esse tipo; para autenticar usuários, use SingleSignature. |
Ler o certificado não conta como assinatura: uma sessão SingleSignature pode ler o certificado e
depois assinar uma vez.
Tempo de vida
lifetimeInSeconds pede ao provedor uma sessão com essa duração. Quem decide é o provedor: ele pode
conceder menos e, quando o valor é omitido, aplica o próprio padrão. Peça o que a operação precisa:
alguns minutos para uma assinatura, mais para uma SignatureSession que assina um lote.
Depois que uma sessão expira, o CloudHub responde com o erro SessionExpired a uma chamada que leia
o certificado dela ou assine com ela. Isso pode acontecer já na primeira chamada, se o usuário
demorou a autorizar. Crie
uma nova sessão e envie o usuário ao provedor de novo.
Descoberta
A descoberta é como o CloudHub encontra os provedores que têm um certificado para o signatário. Ao criar uma sessão:
- Com um identificador, o CloudHub consulta todos os provedores em paralelo e devolve só os que têm um certificado para ele. Um provedor que não responde fica de fora, em vez de fazer a chamada falhar. Envie o CPF ou o CNPJ só com os dígitos.
identifierTypediz se o identificador é umCPFou umCNPJ. Quando omitido, um identificador com mais de 11 dígitos é tratado como CNPJ.discover: falsepula as consultas e devolve todos os provedores, passando o identificador a cada um como dica de login (login hint).- Sem identificador, o CloudHub devolve todos os provedores, e o usuário se identifica no
provedor, por exemplo lendo um QR code.
discover: truesem identificador é recusado comIdentifierRequiredToDiscover.
Outras três operações ajudam quando você precisa de mais controle:
GET /api/sessions/services?identifier=…faz a descoberta sem criar uma sessão. Devolve os provedores, mas semauthUrl.GET /api/sessions/services/{name}/availability?identifier=…consulta um único provedor.discoveryAvailable: falsesignifica que o provedor não conseguiu responder; caso contrário,certificateFounddiz se ele tem um certificado para o identificador.POST /api/sessions/services/{name}cria uma sessão em um único provedor, quando o usuário já o escolheu.nameé oserviceNameque o CloudHub devolveu, comoBirdID.
A página API REST detalha cada uma.
URIs de redirecionamento
O CloudHub só devolve usuários a URIs de redirecionamento cadastradas para a sua chave de API. Ele
confere a URI quando você cria a sessão e recusa uma URI não cadastrada com
UnauthorizedRedirectUri. A Lacuna cadastra as URIs ao emitir a chave; uma chave pode ter várias.
Cada entrada tem uma destas formas:
| Entrada cadastrada | Aceita |
|---|---|
https://app.example.com/sign/complete | Exatamente essa URI, sem diferenciar maiúsculas de minúsculas. A query string entra na comparação. |
https://app.example.com | Qualquer URI que comece com esse esquema, host e porta. |
https://*.example.com | Qualquer subdomínio de example.com (mas não o próprio example.com), com o mesmo esquema. Inclua a porta quando não for a padrão: https://*.example.com:8443. |
https://*.example.com/callback | Qualquer subdomínio, mas só quando o caminho for exatamente /callback. |
Se você acrescenta os seus próprios parâmetros à URI de redirecionamento (veja a próxima seção), cadastre uma entrada de host ou com curinga: uma entrada exata deixa de corresponder assim que a query string muda.
Para desenvolvimento local, peça uma entrada como https://localhost:5001 além das de produção.
Como levar os seus dados até o retorno
O usuário sai da sua aplicação para autorizar, então a requisição que volta à sua URI de redirecionamento precisa saber a que ela se refere: qual documento assinar, qual pedido aprovar. Há duas formas de levar essa informação:
- Parâmetros na URI de redirecionamento, como
?fileId=42. Eles voltam inalterados, ao lado desession, e todas as bibliotecas cliente os suportam. O usuário consegue vê-los e alterá-los. customState, uma string que você envia ao criar a sessão. O CloudHub a guarda dentro do estado OAuth criptografado dele, então o usuário não consegue lê-la nem alterá-la, e a devolve emGET /api/sessions/custom-state?session=…. A API REST, o cliente PHP e o cliente Node.js a enviam; os clientes .NET e Java publicados hoje não têm o campo.
De um jeito ou de outro, confira se o que volta se refere a algo que o usuário atual pode assinar.
Lista de verificação de segurança
- Mantenha a chave de API no seu servidor. Nunca a envie a um navegador nem a inclua em um aplicativo móvel. Se ela vazar, peça uma nova ao suporte da Lacuna.
- Confira quem assinou. O usuário pode entrar no provedor com uma conta diferente da esperada. Depois de ler o certificado, compare o CPF (ou CNPJ) dele com o que você pediu.
- Valide o certificado com a sua biblioteca de assinatura. O CloudHub devolve o que o provedor devolve. É o trust arbitrator (PKI SDK) ou o contexto de segurança (Rest PKI) da sua biblioteca de assinatura que decide se o certificado é confiável.
- Trate o valor da sessão como uma credencial, e só o aceite na URI de redirecionamento com a qual você criou a sessão.
- Use HTTPS em toda URI de redirecionamento fora do desenvolvimento local.