docs(sdk): add comprehensive integration examples in sdk/examples
This commit is contained in:
@@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
* Exemplo 01: Login Seguro via Certificado Digital (Challenge-Response)
|
||||||
|
*
|
||||||
|
* Demonstra como autenticar um usuário sem senhas, assinando um desafio emitido
|
||||||
|
* pelo servidor backend da sua aplicação web.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CyberCoreSigner, AgentNotRunningError } from '@cybercore/signer-sdk';
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const signer = new CyberCoreSigner({
|
||||||
|
applicationName: 'Portal Corporativo ERP'
|
||||||
|
});
|
||||||
|
|
||||||
|
// 1. Verifica se o agente desktop está ativo na máquina
|
||||||
|
const isRunning = await signer.isInstalled();
|
||||||
|
if (!isRunning) {
|
||||||
|
console.error('O CyberCore Signer não está em execução. Baixe e abra o aplicativo para continuar.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Conecta e obtém autorização
|
||||||
|
console.log('Conectando ao agente...');
|
||||||
|
await signer.connect();
|
||||||
|
|
||||||
|
// 3. Lista os certificados digitais disponíveis
|
||||||
|
const certs = await signer.getCertificates();
|
||||||
|
console.log(`Foram localizados ${certs.length} certificado(s) no computador:`);
|
||||||
|
certs.forEach((c, idx) => {
|
||||||
|
console.log(`[${idx + 1}] ${c.alias} (${c.certificate_type}) - ${c.subject_name}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (certs.length === 0) {
|
||||||
|
console.warn('Nenhum certificado digital A1 ou token A3 encontrado.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seleciona o primeiro certificado disponível
|
||||||
|
const selectedCert = certs[0];
|
||||||
|
|
||||||
|
// 4. Obtém o desafio emitido pelo seu backend (simulado aqui)
|
||||||
|
const serverChallenge = `auth-session-${Date.now()}-${Math.random().toString(36).substring(2)}`;
|
||||||
|
console.log(`Desafio recebido do servidor: ${serverChallenge}`);
|
||||||
|
|
||||||
|
// 5. Executa a autenticação criptográfica
|
||||||
|
console.log('Solicitando confirmação de login no CyberCore Signer...');
|
||||||
|
const authResult = await signer.authenticate({
|
||||||
|
certificateId: selectedCert.id,
|
||||||
|
challenge: serverChallenge
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Login realizado com sucesso!');
|
||||||
|
console.log('Titular:', authResult.subjectName);
|
||||||
|
console.log('CPF/CNPJ:', authResult.subjectCpfCnpj);
|
||||||
|
console.log('Algoritmo:', authResult.algorithm);
|
||||||
|
console.log('Assinatura do Desafio (Base64):', authResult.signature.substring(0, 32) + '...');
|
||||||
|
|
||||||
|
// O payload retornado deve ser enviado ao seu backend para verificação final:
|
||||||
|
// POST /api/auth/verify-signature { challenge, signature, certificate }
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(err => {
|
||||||
|
if (err instanceof AgentNotRunningError) {
|
||||||
|
console.error('Agente offline!\n' + err.actionableGuidance);
|
||||||
|
} else {
|
||||||
|
console.error(`Erro no login (${err.code}):`, err.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/**
|
||||||
|
* Exemplo 02: Assinatura Desacoplada de Digest (Hash SHA-256)
|
||||||
|
*
|
||||||
|
* Demonstra a arquitetura recomendada do CCSP/1:
|
||||||
|
* O documento NUNCA sai do navegador / servidor da aplicação. Apenas o hash
|
||||||
|
* de 32 bytes (SHA-256) é enviado ao CyberCore Signer para ser assinado localmente.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CyberCoreSigner } from '@cybercore/signer-sdk';
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const signer = new CyberCoreSigner({
|
||||||
|
applicationName: 'Sistema de Prontuário Eletrônico'
|
||||||
|
});
|
||||||
|
|
||||||
|
await signer.connect();
|
||||||
|
|
||||||
|
const certs = await signer.getCertificates();
|
||||||
|
if (certs.length === 0) {
|
||||||
|
throw new Error('Nenhum certificado disponível.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedCert = certs[0];
|
||||||
|
|
||||||
|
// Simula o documento clínico ou contrato gerado na aplicação
|
||||||
|
const documentoTexto = 'Evolução clínica do paciente João da Silva - 20/10/2026. Procedimento sem intercorrências.';
|
||||||
|
|
||||||
|
// 1. Calcula o SHA-256 no próprio ambiente local
|
||||||
|
const digestBase64 = await signer._calculateSha256(documentoTexto);
|
||||||
|
console.log('Hash SHA-256 gerado localmente:', digestBase64);
|
||||||
|
|
||||||
|
// 2. Envia apenas o digest para o CyberCore Signer
|
||||||
|
console.log('Aguardando confirmação do usuário no diálogo nativo...');
|
||||||
|
const result = await signer.sign({
|
||||||
|
certificateId: selectedCert.id,
|
||||||
|
digest: digestBase64,
|
||||||
|
documentName: 'evolucao_clinica_1048.txt',
|
||||||
|
documentType: 'TXT',
|
||||||
|
purpose: 'Assinatura de Prontuário Médico',
|
||||||
|
expiresInSeconds: 120
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Documento assinado com sucesso!');
|
||||||
|
console.log('Status:', result.status);
|
||||||
|
console.log('Request ID:', result.requestId);
|
||||||
|
console.log('Algoritmo:', result.algorithm);
|
||||||
|
console.log('Assinatura (Base64):', result.signature.substring(0, 48) + '...');
|
||||||
|
console.log('Cadeia X.509 recebida:', result.certificateChain.length, 'certificado(s)');
|
||||||
|
|
||||||
|
// 3. Validação preliminar da assinatura no client
|
||||||
|
const isValid = await signer.verify({
|
||||||
|
signature: result.signature,
|
||||||
|
digest: result.digest,
|
||||||
|
certificate: result.certificate
|
||||||
|
});
|
||||||
|
console.log('Assinatura é consistente?', isValid);
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(console.error);
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* Exemplo 03: Assinatura de PDF Completo PAdES com Selo Visual
|
||||||
|
*
|
||||||
|
* Demonstra como assinar um arquivo PDF aplicando o selo gráfico ICP-Brasil ou Gov.br
|
||||||
|
* estilizado pelo CyberCore Signer, retornando o PDF assinado completo em Base64/Blob.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CyberCoreSigner } from '@cybercore/signer-sdk';
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const signer = new CyberCoreSigner({
|
||||||
|
applicationName: 'Assinador de Contratos Online'
|
||||||
|
});
|
||||||
|
|
||||||
|
await signer.connect();
|
||||||
|
|
||||||
|
// 1. Consulta os temas de selos visuais disponíveis no agente
|
||||||
|
const themesResp = await signer.getSealThemes();
|
||||||
|
console.log('Selo padrão configurado:', themesResp.default_seal_id);
|
||||||
|
console.log('Temas disponíveis:', themesResp.seals.map(s => s.name).join(', '));
|
||||||
|
|
||||||
|
// 2. Mock de um PDF de teste (no navegador, normalmente é obtido via <input type="file">)
|
||||||
|
const fakePdfContent = Buffer.from('%PDF-1.4\n1 0 obj\n<< /Type /Catalog >>\nendobj\n%%EOF');
|
||||||
|
|
||||||
|
const certs = await signer.getCertificates();
|
||||||
|
if (certs.length === 0) {
|
||||||
|
console.error('Nenhum certificado para assinatura.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Executa a assinatura com estampa gráfica posicionada no rodapé da última página
|
||||||
|
console.log('Enviando solicitação de assinatura PAdES...');
|
||||||
|
const result = await signer.signPdfDocument({
|
||||||
|
pdf: fakePdfContent,
|
||||||
|
certificateId: certs[0].id,
|
||||||
|
documentName: 'contrato_prestacao_servicos.pdf',
|
||||||
|
position: 'BOTTOM_RIGHT',
|
||||||
|
pageChoice: 'LAST_PAGE',
|
||||||
|
scale: 1.0,
|
||||||
|
visibleSeal: true,
|
||||||
|
sealThemeId: 'icp-brasil',
|
||||||
|
reason: 'Concordância integral com os termos do contrato',
|
||||||
|
location: 'Brasília - DF',
|
||||||
|
cpfDisplayMode: 'MASKED', // Exibe CPF mascarado no carimbo (ex: ***.456.789-**)
|
||||||
|
includeDateTime: true,
|
||||||
|
customExtraInfo: 'Matrícula: 55420-9'
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('PDF assinado com sucesso!');
|
||||||
|
console.log('Titular:', result.subjectName);
|
||||||
|
console.log('CPF/CNPJ:', result.subjectCpfCnpj);
|
||||||
|
console.log('Tamanho do PDF assinado:', result.signedPdf.length, 'caracteres Base64');
|
||||||
|
|
||||||
|
// No navegador web, você pode invocar diretamente:
|
||||||
|
// result.download('contrato_assinado.pdf');
|
||||||
|
// Ou obter os bytes binários para envio via fetch/multipart:
|
||||||
|
const pdfBytes = result.getSignedPdfBytes();
|
||||||
|
console.log('Bytes binários gerados:', pdfBytes.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(console.error);
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
/**
|
||||||
|
* Exemplo 04: Tratamento Estruturado e Acionável de Erros
|
||||||
|
*
|
||||||
|
* Demonstra como capturar cada erro específico do CCSP/1 e orientar
|
||||||
|
* o usuário final com mensagens claras e passos práticos de resolução.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
CyberCoreSigner,
|
||||||
|
AgentNotRunningError,
|
||||||
|
ApplicationNotAuthorizedError,
|
||||||
|
UserCancelledError,
|
||||||
|
CertificateNotFoundError,
|
||||||
|
CertificateExpiredError,
|
||||||
|
CertificateInvalidError,
|
||||||
|
PrivateKeyNotAvailableError,
|
||||||
|
PinRequiredError,
|
||||||
|
PinIncorrectError,
|
||||||
|
TokenNotFoundError,
|
||||||
|
RequestExpiredError,
|
||||||
|
TimeoutError,
|
||||||
|
CyberCoreError
|
||||||
|
} from '@cybercore/signer-sdk';
|
||||||
|
|
||||||
|
async function performSigningOperation(signer, certId, fileDigest) {
|
||||||
|
try {
|
||||||
|
return await signer.sign({
|
||||||
|
certificateId: certId,
|
||||||
|
digest: fileDigest,
|
||||||
|
documentName: 'termo_responsabilidade.pdf'
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof AgentNotRunningError) {
|
||||||
|
// O agente local está fechado ou bloqueado
|
||||||
|
console.warn('⚠️ AGENTE NÃO DETECTADO:');
|
||||||
|
console.warn(err.actionableGuidance);
|
||||||
|
// Sugestão de UI: exibir banner com botão "Baixar CyberCore Signer"
|
||||||
|
} else if (err instanceof ApplicationNotAuthorizedError) {
|
||||||
|
// O usuário clicou em "Rejeitar" no prompt de autorização da aplicação
|
||||||
|
console.warn('⚠️ APLICAÇÃO NÃO AUTORIZADA:');
|
||||||
|
console.warn('O usuário não autorizou este portal no diálogo de segurança do CyberCore Signer.');
|
||||||
|
} else if (err instanceof UserCancelledError) {
|
||||||
|
// O usuário cancelou a assinatura explicitamente na janela nativa
|
||||||
|
console.info('ℹ️ Assinatura cancelada pelo usuário.');
|
||||||
|
} else if (err instanceof CertificateExpiredError) {
|
||||||
|
console.error('❌ CERTIFICADO EXPIRADO: O certificado digital selecionado expirou.');
|
||||||
|
} else if (err instanceof CertificateInvalidError) {
|
||||||
|
console.error('❌ CERTIFICADO INVÁLIDO: O certificado possui estrutura corrompida ou foi revogado.');
|
||||||
|
} else if (err instanceof PrivateKeyNotAvailableError) {
|
||||||
|
console.error('❌ CHAVE PRIVADA INDISPONÍVEL: A chave privada deste certificado não está acessível no computador.');
|
||||||
|
} else if (err instanceof PinIncorrectError) {
|
||||||
|
console.error('❌ PIN INCORRETO: O PIN digitado para o token A3 está errado. Cuidado com o bloqueio do dispositivo.');
|
||||||
|
} else if (err instanceof PinRequiredError) {
|
||||||
|
console.error('❌ PIN OBRIGATÓRIO: É necessário fornecer o PIN do dispositivo criptográfico.');
|
||||||
|
} else if (err instanceof TokenNotFoundError) {
|
||||||
|
console.error('❌ TOKEN NÃO ENCONTRADO: O token USB ou smart card foi removido da porta USB.');
|
||||||
|
} else if (err instanceof RequestExpiredError) {
|
||||||
|
console.warn('⚠️ REQUISIÇÃO EXPIRADA: O tempo limite para confirmação do usuário esgotou.');
|
||||||
|
} else if (err instanceof TimeoutError) {
|
||||||
|
console.error('⏱️ TIMEOUT: O agente demorou mais tempo que o esperado para responder.');
|
||||||
|
} else if (err instanceof CyberCoreError) {
|
||||||
|
console.error(`⚠️ ERRO DO PROTOCOLO CCSP (${err.code}):`, err.message);
|
||||||
|
} else {
|
||||||
|
console.error('Erro desconhecido:', err);
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const signer = new CyberCoreSigner({ timeoutMs: 15000 });
|
||||||
|
const fakeDigest = Buffer.alloc(32, 9).toString('base64');
|
||||||
|
|
||||||
|
console.log('Testando tratamento de erros com certificado simulado expirado...');
|
||||||
|
try {
|
||||||
|
await performSigningOperation(signer, 'cert-expirado', fakeDigest);
|
||||||
|
} catch {
|
||||||
|
console.log('Tratamento de exceções concluído com sucesso.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(() => {});
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
# Exemplos de Integração — CyberCore Signer Web SDK
|
||||||
|
|
||||||
|
Este diretório contém exemplos práticos e prontos para uso demonstrando os principais fluxos de integração do `@cybercore/signer-sdk` com aplicações web.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📁 Arquivos de Exemplo
|
||||||
|
|
||||||
|
| Arquivo | Descrição |
|
||||||
|
| :--- | :--- |
|
||||||
|
| [`01-crypto-login.js`](./01-crypto-login.js) | Autenticação segura por desafio criptográfico (Challenge-Response) sem senhas. |
|
||||||
|
| [`02-sign-digest.js`](./02-sign-digest.js) | Assinatura desacoplada em que o navegador calcula o hash SHA-256 e o CyberCore assina apenas o digest. |
|
||||||
|
| [`03-sign-pades.js`](./03-sign-pades.js) | Assinatura digital completa de PDF (PAdES) com selo gráfico posicionado (ICP-Brasil / Gov.br). |
|
||||||
|
| [`04-error-handling.js`](./04-error-handling.js) | Captura e tratamento estruturado de todos os erros do protocolo CCSP/1 com guia acionável. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Como Executar
|
||||||
|
|
||||||
|
### Pré-requisitos
|
||||||
|
1. Agente desktop **CyberCore Signer** em execução na máquina (`127.0.0.1:21325`).
|
||||||
|
2. Node.js 18+ instalado.
|
||||||
|
|
||||||
|
### Execução direta via Node.js
|
||||||
|
Como o pacote usa módulos ES (`"type": "module"`), você pode testar diretamente com:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Executar exemplo de login
|
||||||
|
node examples/01-crypto-login.js
|
||||||
|
|
||||||
|
# Executar exemplo de assinatura de digest
|
||||||
|
node examples/02-sign-digest.js
|
||||||
|
|
||||||
|
# Executar exemplo de assinatura PAdES com selo
|
||||||
|
node examples/03-sign-pades.js
|
||||||
|
|
||||||
|
# Executar exemplo de tratamento de erros
|
||||||
|
node examples/04-error-handling.js
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💡 Uso em Projetos Frontend (React, Vue, Angular, Svelte, Vanilla)
|
||||||
|
|
||||||
|
Instale o pacote a partir do seu registry npm privado:
|
||||||
|
```bash
|
||||||
|
npm install @cybercore/signer-sdk
|
||||||
|
```
|
||||||
|
|
||||||
|
Importe os métodos e tipos em qualquer componente ou serviço web:
|
||||||
|
```typescript
|
||||||
|
import { CyberCoreSigner, AgentNotRunningError } from '@cybercore/signer-sdk';
|
||||||
|
|
||||||
|
const signer = new CyberCoreSigner({
|
||||||
|
applicationName: 'Meu Sistema Web'
|
||||||
|
});
|
||||||
|
|
||||||
|
async function assinarDocumento(arquivoPdf: File, certId: string) {
|
||||||
|
try {
|
||||||
|
const resultado = await signer.signPdfDocument({
|
||||||
|
pdf: arquivoPdf,
|
||||||
|
certificateId: certId,
|
||||||
|
position: 'BOTTOM_RIGHT',
|
||||||
|
sealThemeId: 'icp-brasil'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Baixa o documento assinado no navegador
|
||||||
|
resultado.download('documento_assinado.pdf');
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof AgentNotRunningError) {
|
||||||
|
alert(err.actionableGuidance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user