@certysign/sdk 1.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,183 @@
1
+ /**
2
+ * @fileoverview SigningSessionResource — multi-recipient hash-based signing
3
+ *
4
+ * Manages signing sessions where:
5
+ * - Document hashes are registered (documents stay local)
6
+ * - Recipients are added with sequential or parallel signing order
7
+ * - OTP verification is required before each recipient can sign
8
+ * - HSM signs the hash for each verified recipient
9
+ *
10
+ * Covers:
11
+ * - create() Create a signing session
12
+ * - get() Get session status
13
+ * - list() List sessions
14
+ * - sendOtp() Send OTP to a recipient
15
+ * - verifyOtp() Verify recipient OTP
16
+ * - recipientSign() Recipient signs after OTP verification
17
+ */
18
+
19
+ 'use strict';
20
+
21
+ class SigningSessionResource {
22
+ /** @param {import('./HttpClient').HttpClient} http */
23
+ constructor(http) {
24
+ this._http = http;
25
+ }
26
+
27
+ /**
28
+ * Create a signing session with document hashes and recipients.
29
+ *
30
+ * @param {CreateSessionOptions} options
31
+ * @returns {Promise<Object>}
32
+ *
33
+ * @example
34
+ * const session = await client.sessions.create({
35
+ * name: 'Q1 Contract',
36
+ * documents: [
37
+ * { hash: 'abc123...', fileName: 'contract.pdf', hashAlgorithm: 'sha256' }
38
+ * ],
39
+ * recipients: [
40
+ * { email: 'ceo@example.com', name: 'Jane CEO', role: 'signer', order: 1 },
41
+ * { email: 'cfo@example.com', name: 'John CFO', role: 'signer', order: 2 }
42
+ * ],
43
+ * signingOrder: 'sequential'
44
+ * });
45
+ *
46
+ * console.log(session.data._id); // Session ID
47
+ * console.log(session.data.recipients[0].recipientId); // Use for OTP/sign
48
+ */
49
+ create(options) {
50
+ const {
51
+ name,
52
+ description,
53
+ documents,
54
+ recipients,
55
+ signingOrder = 'parallel',
56
+ expiresAt
57
+ } = options;
58
+
59
+ if (!name) throw new Error('create: name is required');
60
+ if (!documents?.length) throw new Error('create: documents array is required');
61
+ if (!recipients?.length) throw new Error('create: recipients array is required');
62
+
63
+ return this._http.post('/sdk/v1/signing-sessions', {
64
+ data: { name, description, documents, recipients, signingOrder, expiresAt }
65
+ });
66
+ }
67
+
68
+ /**
69
+ * Get a signing session by ID.
70
+ *
71
+ * @param {string} sessionId
72
+ * @returns {Promise<Object>}
73
+ */
74
+ get(sessionId) {
75
+ if (!sessionId) throw new Error('get: sessionId is required');
76
+ return this._http.get(`/sdk/v1/signing-sessions/${encodeURIComponent(sessionId)}`);
77
+ }
78
+
79
+ /**
80
+ * List signing sessions with pagination.
81
+ *
82
+ * @param {Object} [options]
83
+ * @param {number} [options.page=1]
84
+ * @param {number} [options.limit=20]
85
+ * @param {string} [options.status]
86
+ * @returns {Promise<Object>}
87
+ */
88
+ list(options = {}) {
89
+ return this._http.get('/sdk/v1/signing-sessions', { params: options });
90
+ }
91
+
92
+ /**
93
+ * Send an OTP verification code to a recipient's email.
94
+ *
95
+ * @param {string} sessionId
96
+ * @param {string} recipientId
97
+ * @returns {Promise<Object>}
98
+ *
99
+ * @example
100
+ * await client.sessions.sendOtp(sessionId, recipientId);
101
+ * // Recipient receives a 6-digit code via email
102
+ */
103
+ sendOtp(sessionId, recipientId) {
104
+ if (!sessionId) throw new Error('sendOtp: sessionId is required');
105
+ if (!recipientId) throw new Error('sendOtp: recipientId is required');
106
+ return this._http.post(
107
+ `/sdk/v1/signing-sessions/${encodeURIComponent(sessionId)}/recipients/${encodeURIComponent(recipientId)}/send-otp`
108
+ );
109
+ }
110
+
111
+ /**
112
+ * Verify a recipient's OTP code.
113
+ * On success, returns a short-lived signing token.
114
+ *
115
+ * @param {string} sessionId
116
+ * @param {string} recipientId
117
+ * @param {string} code - 6-digit OTP from email
118
+ * @returns {Promise<Object>} - { signingToken, expiresAt }
119
+ *
120
+ * @example
121
+ * const result = await client.sessions.verifyOtp(sessionId, recipientId, '123456');
122
+ * const signingToken = result.data.signingToken;
123
+ */
124
+ verifyOtp(sessionId, recipientId, code) {
125
+ if (!sessionId) throw new Error('verifyOtp: sessionId is required');
126
+ if (!recipientId) throw new Error('verifyOtp: recipientId is required');
127
+ if (!code) throw new Error('verifyOtp: code is required');
128
+
129
+ return this._http.post(
130
+ `/sdk/v1/signing-sessions/${encodeURIComponent(sessionId)}/recipients/${encodeURIComponent(recipientId)}/verify-otp`,
131
+ { data: { code: String(code) } }
132
+ );
133
+ }
134
+
135
+ /**
136
+ * Recipient signs all documents in the session.
137
+ * Requires the signing token obtained from verifyOtp().
138
+ *
139
+ * @param {string} sessionId
140
+ * @param {string} recipientId
141
+ * @param {string} signingToken - Token from verifyOtp()
142
+ * @returns {Promise<Object>} - { signedDocuments, certificate, chain }
143
+ *
144
+ * @example
145
+ * const result = await client.sessions.recipientSign(
146
+ * sessionId, recipientId, signingToken
147
+ * );
148
+ *
149
+ * // Use SignatureEmbedder to embed signatures into local documents
150
+ * for (const doc of result.data.signedDocuments) {
151
+ * const signedPdf = await client.embedder.embedInPdf(pdfBuffer, {
152
+ * signature: doc.signature,
153
+ * certificate: result.data.certificate,
154
+ * chain: result.data.chain,
155
+ * reason: 'Contract signing',
156
+ * signerName: 'Jane CEO'
157
+ * });
158
+ * fs.writeFileSync(`./signed-${doc.fileName}`, signedPdf);
159
+ * }
160
+ */
161
+ recipientSign(sessionId, recipientId, signingToken) {
162
+ if (!sessionId) throw new Error('recipientSign: sessionId is required');
163
+ if (!recipientId) throw new Error('recipientSign: recipientId is required');
164
+ if (!signingToken) throw new Error('recipientSign: signingToken is required');
165
+
166
+ return this._http.post(
167
+ `/sdk/v1/signing-sessions/${encodeURIComponent(sessionId)}/recipients/${encodeURIComponent(recipientId)}/sign`,
168
+ { data: { signingToken } }
169
+ );
170
+ }
171
+ }
172
+
173
+ /**
174
+ * @typedef {Object} CreateSessionOptions
175
+ * @property {string} name - Session name
176
+ * @property {string} [description]
177
+ * @property {{ hash: string, fileName: string, hashAlgorithm?: string, mimeType?: string }[]} documents
178
+ * @property {{ email: string, name: string, role?: string, order?: number }[]} recipients
179
+ * @property {'sequential'|'parallel'} [signingOrder='parallel']
180
+ * @property {string} [expiresAt] - ISO date
181
+ */
182
+
183
+ module.exports = { SigningSessionResource };
@@ -1,198 +0,0 @@
1
- /**
2
- * Full PKI Certificate Lifecycle Example
3
- *
4
- * Demonstrates the complete X.509 certificate workflow:
5
- * 1. Issue a per-document certificate
6
- * 2. Check current status (should be 'good')
7
- * 3. Verify the certificate (chain, revocation, validity)
8
- * 4. Download CA chain and save it locally
9
- * 5. Perform a point-in-time verification
10
- * 6. Fetch and cache the CRL locally
11
- * 7. Check a serial number directly against the CRL JSON
12
- * 8. Perform an OCSP query
13
- *
14
- * This pattern is used by HIE systems that need to:
15
- * - Verify document signing certificates before granting access
16
- * - Cache CRL/OCSP responses to meet response time SLAs
17
- *
18
- * Prerequisites:
19
- * npm install @certysign/sdk
20
- * export CERTYSIGN_PUBLIC_KEY="cs_pk_..."
21
- * export CERTYSIGN_SECRET_KEY="cs_sk_..."
22
- */
23
-
24
- 'use strict';
25
-
26
- const fs = require('fs');
27
- const path = require('path');
28
- const { CertySignClient, CertySignError } = require('@certysign/sdk');
29
-
30
- const client = new CertySignClient({
31
- publicKey: process.env.CERTYSIGN_PUBLIC_KEY,
32
- secretKey: process.env.CERTYSIGN_SECRET_KEY,
33
- environment: 'production'
34
- });
35
-
36
- async function runCertificateFlow() {
37
- console.log('\n[PKI] CertySign Certificate Lifecycle Demo');
38
- console.log('─'.repeat(60));
39
-
40
- // ─────────────────────────────────────────────────────────────────────────
41
- // 1. Issue a certificate
42
- // ─────────────────────────────────────────────────────────────────────────
43
- console.log('\n[PKI] 1. Issuing X.509 certificate...');
44
-
45
- const issueResult = await client.certificates.issue({
46
- commonName: 'HIE Integration Node — KNH',
47
- organisation: 'Kenyatta National Hospital',
48
- country: 'KE',
49
- state: 'Nairobi',
50
- locality: 'Nairobi',
51
- email: 'pki@knh.or.ke',
52
- validityDays: 365,
53
- metadata: { system: 'HIE-NODE-KNH-PROD', issuedAt: new Date().toISOString() }
54
- });
55
-
56
- const cert = issueResult.data;
57
- console.log(` serialNumber : ${cert.serialNumber}`);
58
- console.log(` fingerprint : ${cert.fingerprint}`);
59
- console.log(` validFrom : ${cert.validFrom}`);
60
- console.log(` validUntil : ${cert.validUntil}`);
61
- console.log(` Subject CN : ${cert.subject?.cn}`);
62
-
63
- // Store private key securely (shown ONCE — CertySign does not retain it)
64
- const keyPath = path.join(__dirname, 'hie-node-knh.key.pem');
65
- fs.writeFileSync(keyPath, cert.privateKey, { mode: 0o600 });
66
- console.log(` privateKey → ${keyPath} (mode 600)`);
67
-
68
- const certPath = path.join(__dirname, 'hie-node-knh.crt.pem');
69
- fs.writeFileSync(certPath, cert.pemCertificate);
70
- console.log(` certificate → ${certPath}`);
71
-
72
- const { serialNumber } = cert;
73
-
74
- // ─────────────────────────────────────────────────────────────────────────
75
- // 2. Real-time status check (should be 'good')
76
- // ─────────────────────────────────────────────────────────────────────────
77
- console.log('\n[PKI] 2. Certificate status check...');
78
- const statusResult = await client.certificates.status(serialNumber);
79
- const status = statusResult.data;
80
- console.log(` status : ${status.status}`);
81
- if (status.status === 'revoked') {
82
- console.log(` revocationDate : ${status.revocationDate}`);
83
- console.log(` reason : ${status.revocationReason}`);
84
- }
85
-
86
- // ─────────────────────────────────────────────────────────────────────────
87
- // 3. Full certificate verification (chain + revocation)
88
- // ─────────────────────────────────────────────────────────────────────────
89
- console.log('\n[PKI] 3. Full certificate verification...');
90
- const verifyResult = await client.certificates.verify(serialNumber);
91
- const verification = verifyResult.data;
92
- console.log(` valid : ${verification.valid}`);
93
- console.log(` chainVerified : ${verification.chainVerified}`);
94
- console.log(` revocationChecked : ${verification.revocationChecked}`);
95
- console.log(` certStatus : ${verification.certStatus}`);
96
-
97
- // ─────────────────────────────────────────────────────────────────────────
98
- // 4. Download CA chain and install in local trust store
99
- // ─────────────────────────────────────────────────────────────────────────
100
- console.log('\n[PKI] 4. Downloading CA chain...');
101
- const chainPem = await client.pki.chain();
102
- const chainPath = path.join(__dirname, 'certysign-chain.pem');
103
- fs.writeFileSync(chainPath, chainPem);
104
- console.log(` CA chain → ${chainPath}`);
105
- console.log(` (${chainPem.split('-----BEGIN CERTIFICATE-----').length - 1} cert(s) in bundle)`);
106
-
107
- // ─────────────────────────────────────────────────────────────────────────
108
- // 5. Point-in-time verification
109
- // Used by HIE to check if a cert was valid at the time a document was signed
110
- // ─────────────────────────────────────────────────────────────────────────
111
- console.log('\n[PKI] 5. Point-in-time verification...');
112
- const signingTime = new Date(Date.now() - 5 * 60 * 1000); // 5 minutes ago
113
- const ptResult = await client.certificates.verify(serialNumber, signingTime);
114
- console.log(` valid at ${signingTime.toISOString()} : ${ptResult.data.valid}`);
115
-
116
- // ─────────────────────────────────────────────────────────────────────────
117
- // 6. Fetch and cache CRL (JSON format for application-level checks)
118
- // ─────────────────────────────────────────────────────────────────────────
119
- console.log('\n[PKI] 6. Fetching CRL (JSON)...');
120
- const crlJson = await client.pki.crl('json');
121
- const crl = crlJson.data;
122
- console.log(` issuer : ${crl.issuer}`);
123
- console.log(` thisUpdate : ${crl.thisUpdate}`);
124
- console.log(` nextUpdate : ${crl.nextUpdate}`);
125
- console.log(` revokedCount : ${crl.revokedCount}`);
126
-
127
- // Build a Set of revoked serial numbers for fast in-process lookups
128
- const revokedSet = new Set((crl.crl || []).map(e => e.serialNumber));
129
- console.log(` Revoked set : ${revokedSet.size} entries loaded`);
130
-
131
- // Cache on disk until nextUpdate
132
- fs.writeFileSync(
133
- path.join(__dirname, 'crl-cache.json'),
134
- JSON.stringify({ ...crl, cachedAt: new Date().toISOString() }, null, 2)
135
- );
136
-
137
- // Also download DER CRL for systems that need binary format
138
- console.log('\n[PKI] 6b. Downloading DER CRL...');
139
- const crlDer = await client.pki.crl('der');
140
- fs.writeFileSync(path.join(__dirname, 'certysign-intermediate.crl'), crlDer);
141
- console.log(` certysign-intermediate.crl — ${crlDer.length} bytes`);
142
-
143
- // ─────────────────────────────────────────────────────────────────────────
144
- // 7. In-process revocation check using cached CRL
145
- // ─────────────────────────────────────────────────────────────────────────
146
- console.log('\n[PKI] 7. Local CRL check...');
147
- const isRevoked = revokedSet.has(serialNumber);
148
- console.log(` Serial ${serialNumber.slice(0, 12)}... : ${isRevoked ? 'REVOKED' : 'not in CRL'}`);
149
-
150
- // ─────────────────────────────────────────────────────────────────────────
151
- // 8. OCSP query — freshest revocation status
152
- // ─────────────────────────────────────────────────────────────────────────
153
- console.log('\n[PKI] 8. OCSP query...');
154
- const ocspResult = await client.pki.ocsp(serialNumber, 'json');
155
- const ocsp = ocspResult.data;
156
- console.log(` status : ${ocsp.status}`);
157
- console.log(` producedAt : ${ocsp.producedAt}`);
158
- console.log(` responderId : ${ocsp.responderId}`);
159
- console.log(` nextUpdate : ${ocsp.nextUpdate}`);
160
- console.log(` signatureAlgo : ${ocsp.signatureAlgorithm}`);
161
- if (ocsp.certIdHash) {
162
- console.log(` CertID algo : ${ocsp.certIdHash.algorithm}`);
163
- console.log(` issuerNameHash : ${ocsp.certIdHash.issuerNameHash?.slice(0, 16)}...`);
164
- }
165
-
166
- // ─────────────────────────────────────────────────────────────────────────
167
- // 9. CA Info — inspect the trust hierarchy
168
- // ─────────────────────────────────────────────────────────────────────────
169
- console.log('\n[PKI] 9. CA hierarchy info...');
170
- const info = await client.pki.info();
171
- const caInfo = info.data;
172
- console.log(` Root CA : ${caInfo.rootCA?.subject?.commonName}`);
173
- console.log(` Root CA expiry : ${caInfo.rootCA?.validity?.notAfter}`);
174
- console.log(` Inter CA : ${caInfo.intermediateCA?.subject?.commonName}`);
175
- console.log(` Inter CA expiry: ${caInfo.intermediateCA?.validity?.notAfter}`);
176
- console.log(` CRL URL : ${caInfo.intermediateCA?.crlUrl}`);
177
- console.log(` OCSP URL : ${caInfo.intermediateCA?.ocspUrl}`);
178
- console.log(` Initialised : ${caInfo.status?.initialized}`);
179
-
180
- console.log('\n[PKI] ✓ Certificate lifecycle demo complete');
181
- console.log('─'.repeat(60));
182
- }
183
-
184
- // ── Run ───────────────────────────────────────────────────────────────────────
185
-
186
- (async () => {
187
- try {
188
- await runCertificateFlow();
189
- } catch (err) {
190
- if (err instanceof CertySignError) {
191
- console.error(`\n[PKI] API Error (${err.statusCode}): ${err.message}`);
192
- if (err.details) console.error('[PKI] Details:', JSON.stringify(err.details, null, 2));
193
- } else {
194
- console.error('\n[PKI] Error:', err.message);
195
- }
196
- process.exit(1);
197
- }
198
- })();
@@ -1,208 +0,0 @@
1
- /**
2
- * DHA Kenya — Health Claims Quick-Sign Integration
3
- *
4
- * This example demonstrates the standard integration pattern for the
5
- * Department of Health Affairs (DHA) Kenya claims processing system.
6
- *
7
- * Workflow:
8
- * 1. Load a health claim PDF
9
- * 2. Sign it using quickSign() — creates envelope, uploads, signs in one call
10
- * 3. Issue a per-document X.509 certificate bound to the claim
11
- * 4. Store the envelopeId and certificate serial number in the claims DB
12
- * 5. Optionally verify the certificate via OCSP before archiving
13
- *
14
- * Prerequisites:
15
- * npm install @certysign/sdk
16
- * export CERTYSIGN_PUBLIC_KEY="cs_pk_..."
17
- * export CERTYSIGN_SECRET_KEY="cs_sk_..."
18
- */
19
-
20
- 'use strict';
21
-
22
- const fs = require('fs');
23
- const path = require('path');
24
- const { CertySignClient } = require('@certysign/sdk');
25
-
26
- // ── Initialise client ────────────────────────────────────────────────────────
27
-
28
- const client = new CertySignClient({
29
- publicKey: process.env.CERTYSIGN_PUBLIC_KEY,
30
- secretKey: process.env.CERTYSIGN_SECRET_KEY,
31
- environment: 'production' // 'staging' for UAT
32
- });
33
-
34
- // ── Simulated DHA claim record ───────────────────────────────────────────────
35
-
36
- const claim = {
37
- id: 'CLM-2026-003421',
38
- patientName: 'John Kamau Njoroge',
39
- facility: 'Kenyatta National Hospital',
40
- approvedBy: 'Dr. Amina Okonkwo',
41
- approverEmail: 'amina.okonkwo@dha.go.ke',
42
- department: 'Primary Health Outreach',
43
- amount: 45_000 // KES
44
- };
45
-
46
- // ── Main integration function ────────────────────────────────────────────────
47
-
48
- async function processClaim(claim) {
49
- console.log(`\n[DHA] Processing claim ${claim.id} — ${claim.facility}`);
50
- console.log('─'.repeat(60));
51
-
52
- // Step 1: Load the generated claim PDF (your system produces this)
53
- const pdfPath = path.join(__dirname, 'sample-claim.pdf');
54
- let pdfBuffer;
55
- try {
56
- pdfBuffer = fs.readFileSync(pdfPath);
57
- } catch {
58
- // Create a minimal placeholder for demo purposes
59
- pdfBuffer = Buffer.from('%PDF-1.4 sample claim document');
60
- console.warn('[DHA] Using placeholder PDF — replace with real claim PDF in production');
61
- }
62
-
63
- // ─────────────────────────────────────────────────────────────────────────
64
- // Step 2: Sign the claim document
65
- // ─────────────────────────────────────────────────────────────────────────
66
- console.log('\n[DHA] Signing claim document...');
67
-
68
- const signResult = await client.sign.quickSign({
69
- document: pdfBuffer,
70
- filename: `claim-${claim.id}.pdf`,
71
- signerName: claim.approvedBy,
72
- signerEmail: claim.approverEmail,
73
- reason: `Health claim approval — DHA Kenya (${claim.id})`,
74
- location: 'Nairobi, Kenya',
75
- metadata: {
76
- claimId: claim.id,
77
- patientName: claim.patientName,
78
- facility: claim.facility,
79
- department: claim.department,
80
- amount: claim.amount,
81
- currency: 'KES',
82
- system: 'DHA-CLAIMS-PROD'
83
- }
84
- });
85
-
86
- const { envelopeId, certificate: sigCert } = signResult.data;
87
- console.log(`[DHA] ✓ Document signed`);
88
- console.log(` envelopeId : ${envelopeId}`);
89
- console.log(` sigCert SN : ${sigCert?.serialNumber || '(embedded in PDF)'}`);
90
-
91
- // ─────────────────────────────────────────────────────────────────────────
92
- // Step 3: Issue a per-document X.509 certificate for the claim
93
- // This cert is used by the HIE system to grant document access
94
- // ─────────────────────────────────────────────────────────────────────────
95
- console.log('\n[DHA] Issuing per-document X.509 certificate...');
96
-
97
- const certResult = await client.certificates.issue({
98
- commonName: `DHA-${claim.id}`,
99
- organisation: 'Department of Health Affairs Kenya',
100
- country: 'KE',
101
- state: 'Nairobi',
102
- locality: 'Nairobi',
103
- email: claim.approverEmail,
104
- validityDays: 365,
105
- metadata: {
106
- claimId: claim.id,
107
- envelopeId,
108
- facility: claim.facility,
109
- issuedAt: new Date().toISOString()
110
- }
111
- });
112
-
113
- const cert = certResult.data;
114
- console.log(`[DHA] ✓ Certificate issued`);
115
- console.log(` serialNumber : ${cert.serialNumber}`);
116
- console.log(` fingerprint : ${cert.fingerprint}`);
117
- console.log(` validUntil : ${cert.validUntil}`);
118
- console.log(` privateKey : [SHOW ONCE — store securely]`);
119
-
120
- // ─────────────────────────────────────────────────────────────────────────
121
- // Step 4: Verify the certificate is good via OCSP
122
- // (optional but recommended before archiving)
123
- // ─────────────────────────────────────────────────────────────────────────
124
- console.log('\n[DHA] Verifying certificate via OCSP...');
125
-
126
- const ocspResult = await client.pki.ocsp(cert.serialNumber, 'json');
127
- const ocspStatus = ocspResult.data;
128
-
129
- console.log(`[DHA] OCSP status : ${ocspStatus.status}`);
130
- console.log(` responderId : ${ocspStatus.responderId}`);
131
- console.log(` nextUpdate : ${ocspStatus.nextUpdate}`);
132
-
133
- if (ocspStatus.status !== 'good') {
134
- throw new Error(`[DHA] Unexpected OCSP status: ${ocspStatus.status}`);
135
- }
136
-
137
- // ─────────────────────────────────────────────────────────────────────────
138
- // Step 5: Store claim record with signing proof
139
- // ─────────────────────────────────────────────────────────────────────────
140
- const claimRecord = {
141
- claimId: claim.id,
142
- envelopeId,
143
- certificateSerial: cert.serialNumber,
144
- certificateExpiry: cert.validUntil,
145
- signedAt: new Date().toISOString(),
146
- status: 'SIGNED',
147
- // Store PEM cert for offline verification (NOT the private key in production DB)
148
- pemCertificate: cert.pemCertificate
149
- };
150
-
151
- console.log('\n[DHA] ✓ Claim processed successfully');
152
- console.log('─'.repeat(60));
153
- console.log('[DHA] Claim record:', JSON.stringify(claimRecord, null, 2));
154
-
155
- return claimRecord;
156
- }
157
-
158
- // ── Retrieve and verify an archived claim ────────────────────────────────────
159
-
160
- async function verifyClaim(claimRecord) {
161
- console.log(`\n[DHA] Verifying archived claim ${claimRecord.claimId}`);
162
- console.log('─'.repeat(60));
163
-
164
- // Check signing envelope still has valid signatures
165
- const envelopeResult = await client.sign.verifyById(claimRecord.envelopeId);
166
- const env = envelopeResult.data;
167
-
168
- console.log(`[DHA] Envelope status : ${env.status}`);
169
- console.log(`[DHA] Signature valid : ${env.valid}`);
170
- console.log(`[DHA] Signed by : ${env.signerName}`);
171
- console.log(`[DHA] Signed at : ${env.signedAt}`);
172
-
173
- // Check certificate status
174
- const certStatus = await client.certificates.status(claimRecord.certificateSerial);
175
- console.log(`[DHA] Certificate status: ${certStatus.data.status}`);
176
-
177
- // Point-in-time verification — was cert valid when signing happened?
178
- const certAtTime = await client.certificates.verify(
179
- claimRecord.certificateSerial,
180
- claimRecord.signedAt
181
- );
182
- console.log(`[DHA] Cert valid-at-signing: ${certAtTime.data.valid}`);
183
-
184
- return {
185
- envelopeValid: env.valid,
186
- certificateStatus: certStatus.data.status,
187
- certValidAtSigning: certAtTime.data.valid
188
- };
189
- }
190
-
191
- // ── Run example ──────────────────────────────────────────────────────────────
192
-
193
- (async () => {
194
- try {
195
- const record = await processClaim(claim);
196
- await verifyClaim(record);
197
- } catch (err) {
198
- if (err.code === 'INVALID_API_KEY') {
199
- console.error('\n[DHA] Authentication failed — check CERTYSIGN_PUBLIC_KEY / CERTYSIGN_SECRET_KEY');
200
- } else if (err.code === 'RATE_LIMIT_EXCEEDED') {
201
- console.error('\n[DHA] Rate limit reached — implement request throttling');
202
- } else {
203
- console.error('\n[DHA] Error:', err.message);
204
- if (err.details) console.error('[DHA] Details:', JSON.stringify(err.details, null, 2));
205
- }
206
- process.exit(1);
207
- }
208
- })();