docs: update documentation files and project README structure
This commit is contained in:
+142
-55
@@ -1,17 +1,30 @@
|
||||
# SDK Web JavaScript (@cybercore/signer-sdk)
|
||||
|
||||
O `@cybercore/signer-sdk` é a biblioteca oficial cliente em JavaScript para integrar aplicações web modernas com o agente desktop **CyberCore Signer** via protocolo **CCSP/1**.
|
||||
> **Manual de Integração de Aplicações Web Modernas com o Agente Desktop CyberCore Signer**
|
||||
|
||||
O **`@cybercore/signer-sdk`** é a biblioteca cliente oficial em JavaScript (TypeScript ready) desenvolvida para permitir que aplicações web modernas (React, Next.js, Vue, Angular ou JavaScript nativo) se comuniquem com o agente desktop **CyberCore Signer** através do protocolo local **CCSP/1**.
|
||||
|
||||
---
|
||||
|
||||
## 1. Instalação e Importação
|
||||
## 1. Instalação e Configuração
|
||||
|
||||
### Via Módulo ES6 (Browser ou Bundler Vite/Webpack/Next.js)
|
||||
### 1.1 Instalação via Gerenciador de Pacotes
|
||||
```bash
|
||||
npm install @cybercore/signer-sdk
|
||||
# ou
|
||||
yarn add @cybercore/signer-sdk
|
||||
# ou
|
||||
pnpm add @cybercore/signer-sdk
|
||||
```
|
||||
|
||||
### 1.2 Inicialização do Cliente
|
||||
```javascript
|
||||
import { CyberCoreSigner } from '@cybercore/signer-sdk';
|
||||
|
||||
const signer = new CyberCoreSigner({
|
||||
applicationName: 'Portal Meu Negócio'
|
||||
applicationName: 'Portal Meu Negócio', // Nome exibido no diálogo nativo do usuário
|
||||
port: 21325, // Porta padrão do agente local (opcional)
|
||||
timeoutMs: 30000 // Timeout padrão para operações (opcional)
|
||||
});
|
||||
```
|
||||
|
||||
@@ -19,99 +32,173 @@ const signer = new CyberCoreSigner({
|
||||
|
||||
## 2. Métodos da API
|
||||
|
||||
### `isInstalled(timeoutMs = 2000): Promise<boolean>`
|
||||
Testa rapidamente se o agente desktop está ativo na porta local `21325`.
|
||||
### 2.1 Verificação de Disponibilidade e Conexão
|
||||
|
||||
### `connect(): Promise<boolean>`
|
||||
Realiza o handshake inicial de autorização. Se for a primeira vez que a aplicação tenta se conectar, abre uma caixa de diálogo nativa no desktop do usuário para concessão de acesso.
|
||||
|
||||
### `getCertificates(): Promise<Array<Object>>`
|
||||
Retorna todos os certificados A1 e A3 disponíveis e operacionais na máquina do usuário.
|
||||
#### `isInstalled(timeoutMs = 2000): Promise<boolean>`
|
||||
Testa de forma rápida e silenciosa se o agente desktop está ativo na máquina do usuário.
|
||||
|
||||
```javascript
|
||||
const certs = await signer.getCertificates();
|
||||
certs.forEach(cert => {
|
||||
console.log(`[${cert.type}] ${cert.common_name} (Validade: ${cert.valid_to})`);
|
||||
const disponivel = await signer.isInstalled();
|
||||
if (!disponivel) {
|
||||
alert('O CyberCore Signer não foi detectado em sua máquina. Por favor, instale o aplicativo para continuar.');
|
||||
}
|
||||
```
|
||||
|
||||
#### `connect(): Promise<boolean>`
|
||||
Inicia o handshake de autorização com o agente. Se a aplicação ainda não foi homologada pelo usuário, abre um diálogo nativo na tela do computador para autorização expressa.
|
||||
|
||||
```javascript
|
||||
const autorizado = await signer.connect();
|
||||
if (!autorizado) {
|
||||
console.warn('O usuário recusou a autorização da aplicação.');
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2.2 Descoberta de Certificados Digitais
|
||||
|
||||
#### `getCertificates(): Promise<Array<CertificateInfo>>`
|
||||
Retorna a lista de certificados digitais válidos e disponíveis na máquina (A1 importados e A3 conectados via USB).
|
||||
|
||||
```javascript
|
||||
const certificados = await signer.getCertificates();
|
||||
|
||||
certificados.forEach(cert => {
|
||||
console.log(`[${cert.type}] ${cert.common_name}`);
|
||||
console.log(`Titular: ${cert.subject_dn}`);
|
||||
console.log(`Validade até: ${cert.valid_to}`);
|
||||
console.log(`CPF/CNPJ: ${cert.cpf || cert.cnpj}`);
|
||||
});
|
||||
```
|
||||
|
||||
### `sign(options): Promise<Object>`
|
||||
Solicita a assinatura digital local de um digest ou conteúdo bruto.
|
||||
---
|
||||
|
||||
### `signPdf(pdfFile, options): Promise<Object>`
|
||||
Assina um documento PDF (PAdES / Assinatura desacoplada). Recebe `File`, `Blob`, `ArrayBuffer` ou `Uint8Array`.
|
||||
Calcula o SHA-256 no próprio navegador e transmite metadados do documento (`PDF`) ao agente para exibição clara na janela de confirmação.
|
||||
### 2.3 Operações de Assinatura Digital
|
||||
|
||||
#### `sign(options): Promise<SignatureResult>`
|
||||
Assina o hash SHA-256 (digest) de qualquer informação. A chave privada nunca sai do computador e apenas o digest é assinado.
|
||||
|
||||
```javascript
|
||||
const pdfResult = await signer.signPdf(fileInput.files[0], {
|
||||
certificateId: certs[0].id
|
||||
const resultado = await signer.sign({
|
||||
certificateId: 'win-4340656a',
|
||||
digest: '47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=', // Hash SHA-256 em Base64
|
||||
documentName: 'contrato_prestacao_servicos.pdf',
|
||||
documentType: 'PDF'
|
||||
});
|
||||
|
||||
console.log('Assinatura gerada (Base64):', resultado.signature);
|
||||
console.log('Certificado do signatário:', resultado.certificate);
|
||||
console.log('Cadeia de certificação:', resultado.certificate_chain);
|
||||
```
|
||||
|
||||
### `signXml(xmlContent, options): Promise<Object>`
|
||||
Assina um documento XML (XMLDSig / NF-e). Aceita string com o XML formatado ou objeto de arquivo `File`/`Blob`.
|
||||
#### `signPdf(file, options): Promise<SignatureResult>`
|
||||
Calcula o hash do documento PDF no próprio navegador e solicita a assinatura ao CyberCore Signer.
|
||||
|
||||
```javascript
|
||||
const xmlResult = await signer.signXml(xmlString, {
|
||||
certificateId: certs[0].id,
|
||||
const inputElement = document.getElementById('arquivo-pdf');
|
||||
const arquivoPdf = inputElement.files[0];
|
||||
|
||||
const resultadoPdf = await signer.signPdf(arquivoPdf, {
|
||||
certificateId: certificados[0].id
|
||||
});
|
||||
|
||||
console.log('PDF assinado digitalmente com sucesso!');
|
||||
```
|
||||
|
||||
#### `signXml(xmlContent, options): Promise<SignatureResult>`
|
||||
Assina o hash de documentos XML ou notas fiscais eletrônicas (NF-e, CT-e, MDF-e, XAdES). Aceita `string` com o XML ou arquivo `File`/`Blob`.
|
||||
|
||||
```javascript
|
||||
const xmlString = '<infNFe Id="NFe3524...">...</infNFe>';
|
||||
|
||||
const resultadoXml = await signer.signXml(xmlString, {
|
||||
certificateId: certificados[0].id,
|
||||
documentName: 'nfe-3524.xml'
|
||||
});
|
||||
```
|
||||
|
||||
### `signFile(file, options): Promise<Object>`
|
||||
Assina qualquer arquivo (ZIP, DOCX, imagens, binários, executáveis) gerando pacote de assinatura detached / CAdES.
|
||||
#### `signFile(file, options): Promise<SignatureResult>`
|
||||
Assina arquivos genéricos de qualquer formato (ZIP, DOCX, XLSX, imagens, binários).
|
||||
|
||||
```javascript
|
||||
const fileResult = await signer.signFile(fileInput.files[0], {
|
||||
certificateId: certs[0].id
|
||||
const fileInput = document.getElementById('input-qualquer-arquivo');
|
||||
|
||||
const resultadoArquivo = await signer.signFile(fileInput.files[0], {
|
||||
certificateId: certificados[0].id
|
||||
});
|
||||
```
|
||||
|
||||
### `authenticate(options): Promise<Object>` (ou `login(options)`)
|
||||
Executa login seguro com certificado digital via desafio criptográfico (Challenge-Response).
|
||||
---
|
||||
|
||||
### 2.4 Autenticação e Login Digital (Challenge-Response)
|
||||
|
||||
#### `authenticate(options): Promise<AuthResult>`
|
||||
Realiza o login seguro de um usuário através de seu certificado digital ICP-Brasil. O backend da sua aplicação fornece um desafio único (*challenge / nonce*) e o CyberCore assina esse desafio.
|
||||
|
||||
```javascript
|
||||
const auth = await signer.authenticate({
|
||||
certificateId: certs[0].id,
|
||||
challenge: 'server-nonce-12345'
|
||||
// 1. Obter desafio único do backend da sua aplicação
|
||||
const { challenge } = await fetch('/api/auth/challenge').then(res => res.json());
|
||||
|
||||
// 2. Solicitar assinatura do desafio ao CyberCore Signer
|
||||
const authResult = await signer.authenticate({
|
||||
certificateId: certificados[0].id,
|
||||
challenge: challenge
|
||||
});
|
||||
|
||||
console.log('Bem-vindo,', auth.subjectName);
|
||||
console.log('CPF/CNPJ:', auth.subjectCpfCnpj);
|
||||
console.log('Assinatura do Desafio:', auth.signature);
|
||||
// 3. Enviar a assinatura e o certificado para o backend validar e autenticar a sessão
|
||||
const response = await fetch('/api/auth/validate-login', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
challenge: authResult.challenge,
|
||||
signature: authResult.signature,
|
||||
certificate: authResult.certificate
|
||||
})
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
console.log(`Login aprovado para: ${authResult.subjectName} (${authResult.subjectCpfCnpj})`);
|
||||
window.location.href = '/dashboard';
|
||||
}
|
||||
```
|
||||
|
||||
### `extractSubjectInfo(certificateBase64): { name, cpfCnpj }`
|
||||
Decodifica o certificado X.509 e extrai os dados do titular ICP-Brasil (Nome, CPF ou CNPJ).
|
||||
|
||||
### `verify(options): Promise<boolean>`
|
||||
Validação sintática e criptográfica da integridade da assinatura gerada.
|
||||
|
||||
---
|
||||
|
||||
## 3. Tratamento Estruturado de Erros
|
||||
|
||||
O SDK expõe classes de erro dedicadas que herdam de `CyberCoreError`:
|
||||
O SDK expõe classes de erro dedicadas que herdam de `CyberCoreError`, permitindo tratar cenários de forma elegante na interface:
|
||||
|
||||
```javascript
|
||||
import {
|
||||
UserCancelledError,
|
||||
CertificateNotFoundError,
|
||||
PinIncorrectError,
|
||||
TokenNotFoundError
|
||||
TokenNotFoundError,
|
||||
CertificateNotFoundError,
|
||||
CertificateExpiredError
|
||||
} from '@cybercore/signer-sdk/errors';
|
||||
|
||||
try {
|
||||
await signer.sign({ certificateId: 'cert-1', data: 'Contrato' });
|
||||
} catch (err) {
|
||||
if (err instanceof UserCancelledError) {
|
||||
alert('Você cancelou a assinatura no CyberCore Signer.');
|
||||
} else if (err instanceof PinIncorrectError) {
|
||||
alert('O PIN do token informado está incorreto.');
|
||||
} else if (err instanceof TokenNotFoundError) {
|
||||
alert('Conecte o seu token USB na máquina para assinar.');
|
||||
const res = await signer.sign({
|
||||
certificateId: certId,
|
||||
digest: documentoDigest
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof UserCancelledError) {
|
||||
// Usuário cancelou a assinatura voluntariamente na janela nativa
|
||||
console.info('Operação cancelada pelo usuário.');
|
||||
} else if (error instanceof PinIncorrectError) {
|
||||
// PIN do token A3 incorreto
|
||||
alert('PIN incorreto! Por favor, digite o PIN correto do seu token.');
|
||||
} else if (error instanceof TokenNotFoundError) {
|
||||
// Token USB desconectado
|
||||
alert('Token não encontrado! Por favor, conecte seu dispositivo USB para assinar.');
|
||||
} else if (error instanceof CertificateExpiredError) {
|
||||
// Certificado ultrapassou a data de validade
|
||||
alert('O certificado selecionado expirou. Entre em contato com sua autoridade certificadora.');
|
||||
} else {
|
||||
console.error('Erro na assinatura:', err);
|
||||
// Demais falhas
|
||||
console.error('Falha na operação com o CyberCore Signer:', error);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user