@universis/signer-worker 1.0.5 → 1.5.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.
package/README.md CHANGED
@@ -8,7 +8,7 @@ Install `@universis/signer-worker`
8
8
 
9
9
  npm i @universis/signer-worker
10
10
 
11
- Important note: Verify that [Universis Signer](https://gitlab.com/universis/universis-signer) is already installed as aservice. Follow [installation instructions](https://gitlab.com/universis/universis-signer#installation) to install and configure Universis Signer.
11
+ Important note: Verify that [Universis Signer](https://gitlab.com/universis/universis-signer) is already installed as a service. Follow [installation instructions](https://gitlab.com/universis/universis-signer#installation) to install and configure Universis Signer.
12
12
 
13
13
  ## Configuration
14
14
 
@@ -16,10 +16,96 @@ Configure [Universis api server](https://gitlab.com/universis/universis-api) to
16
16
 
17
17
  {
18
18
  "services": [
19
- ...,
20
19
  {
21
20
  "serviceType": "@universis/signer-worker#SignerWorker"
22
21
  }
23
22
  ]
24
- ...
25
23
  }
24
+
25
+ or configure [Universis api server](https://gitlab.com/universis/universis-api) to use `SignerService` service.
26
+
27
+ {
28
+ "services": [
29
+ {
30
+ "serviceType": "@universis/signer-worker#SignerService"
31
+ }
32
+ ]
33
+ }
34
+
35
+ # Sign documents
36
+
37
+ > `SignerService.signFile(context: DataContext, file: string, contentType?: string, options?: { timestampServer?: string, position?: string, reason?: string })`
38
+
39
+ `SignerService.signFile` can be used for signing *.pdf files using a server instance of [Universis Signer](https://gitlab.com/universis/universis-signer#use-docker-image)
40
+
41
+ For completing sign process, the signer service instance should be configured by adding `settings/universis/signerService` section at application configuration
42
+
43
+ ```json
44
+
45
+ {
46
+ "settings": {
47
+ "universis": {
48
+ "signerService": {
49
+ "user": "user",
50
+ "password": "password",
51
+ "host": "http://localhost:2465"
52
+ }
53
+ }
54
+ }
55
+ }
56
+
57
+ ```
58
+
59
+ where `host` is the URL of the Universis Signer service, `user` and `password` are the credentials for the Universis Signer service.
60
+
61
+ Normally, the Universis Signer service which is running on `http://localhost:2465`, uses a PKCS12 store with secret key and certificate for signing.
62
+ In this case, the `user` and `password` are used to access the PKCS12 store. `SignerService` service will try to get valid certificates,
63
+ and select the first one that is valid for signing. Use the configuration setting `thumbnail` if you want to explicitly define
64
+ the certificate that should be used for signing.
65
+
66
+ ```json
67
+ {
68
+ "settings": {
69
+ "universis": {
70
+ "signerService": {
71
+ "user": "user",
72
+ "password": "password",
73
+ "host": "http://localhost:2465",
74
+ "thumbnail": "2d49be3b03db5d176482640549d5aaede84c925d"
75
+ }
76
+ }
77
+ }
78
+ }
79
+ ```
80
+
81
+ # Sign data
82
+
83
+ > `SignerService.sign(context: DataContext, data: unknown, options?: { timestampServer?: string, reason?: string })`
84
+
85
+ `SignerService.sign` method can be used for signing data.
86
+ The data can be any JSON-serializable object, and the method will return a signed version of the data.
87
+
88
+ The `options` parameter can include a `timestampServer` URL for timestamping the signature, a `position` string to specify the position of the signature in the document,
89
+ and a `reason` string to specify the reason for signing.
90
+
91
+ # Example
92
+
93
+ ```typescript
94
+ import {SignerService} from '@universis/signer-worker';
95
+
96
+ function signJson(context: DataContext, data: unknown) {
97
+ const signerService = context.getApplication().getService(SignerService);
98
+ return signerService.sign(context, data, {
99
+ timestampServer: "http://timestamp.server.com",
100
+ reason: "Signing example data"
101
+ });
102
+ }
103
+
104
+ signJson(context, { message: "Hello, World!" })
105
+ .then(signedData => {
106
+ console.log("Signed data:", signedData);
107
+ })
108
+ .catch(error => {
109
+ console.error("Error signing data:", error);
110
+ });
111
+ ```
@@ -0,0 +1,72 @@
1
+ /// <reference types="node" />
2
+ import { ApplicationBase, ApplicationService } from '@themost/common';
3
+ import { ExpressDataContext } from '@themost/express';
4
+ import { Certificate, JsonWebKey, VerifySignatureResult, SignJsonSignatureResult, SignerServiceImpl } from './SignerServiceImpl';
5
+ import { TOTPOptions } from 'xotp';
6
+ export declare class SignerService extends ApplicationService implements SignerServiceImpl {
7
+ protected options?: {
8
+ host?: string;
9
+ user?: string;
10
+ password?: string;
11
+ secret?: string;
12
+ thumbprint?: string;
13
+ inspector?: {
14
+ text?: string[];
15
+ textPosition: string;
16
+ width?: number;
17
+ height?: number;
18
+ };
19
+ };
20
+ constructor(app: ApplicationBase);
21
+ /**
22
+ * @param {ExpressDataContext} context The underlying data context
23
+ * @param {*} data The data to be verified
24
+ * @param {{filename: string, contentType: string}=} options Extra attributes (optional)
25
+ * @returns {Promise<VerifySignatureResult[]>}
26
+ */
27
+ verify(context: ExpressDataContext, data: unknown, options?: {
28
+ filename: string;
29
+ contentType: string;
30
+ }): Promise<VerifySignatureResult[]>;
31
+ getJwks(): Promise<JsonWebKey[]>;
32
+ getCerts(): Promise<Certificate[]>;
33
+ /**
34
+ * Generates a one-time password using the configured secret.
35
+ */
36
+ generateVerificationCode(options?: TOTPOptions): string;
37
+ /**
38
+ * @param {ExpressDataContext} context The underlying data context
39
+ * @param {*} data Any JSON object to be signed
40
+ * @param {{timestampServer?:string}} extraOptions
41
+ * @returns {Promise<SignJsonSignatureResult>} An array of signatures
42
+ */
43
+ sign(context: ExpressDataContext, data: unknown, extraOptions?: {
44
+ timestampServer?: string;
45
+ reason?: string;
46
+ }): Promise<SignJsonSignatureResult>;
47
+ signFile(context: ExpressDataContext, file: string, contentType?: string, extraOptions?: {
48
+ page?: number;
49
+ position?: number[];
50
+ timestampServer?: string;
51
+ reason?: string;
52
+ }): Promise<Buffer>;
53
+ verifyFile(context: ExpressDataContext, file: string, options?: {
54
+ filename: string;
55
+ contentType: string;
56
+ }): Promise<VerifySignatureResult[]>;
57
+ inspect(context: ExpressDataContext, file: string, options?: {
58
+ filename: string;
59
+ contentType: string;
60
+ }): Promise<{
61
+ page: number;
62
+ position: number[];
63
+ text: string;
64
+ }>;
65
+ requiresVerificationCode(): boolean;
66
+ protected signFinal<T>(context: ExpressDataContext, file: string, contentType?: string, extraOptions?: {
67
+ position?: number[];
68
+ page?: number;
69
+ timestampServer?: string;
70
+ reason?: string;
71
+ }): Promise<T>;
72
+ }
@@ -0,0 +1,61 @@
1
+ /// <reference types="node" />
2
+ import { ExpressDataContext } from '@themost/express';
3
+ export declare interface Certificate {
4
+ version: number;
5
+ subjectDN: string;
6
+ sigAlgName?: string;
7
+ sigAlgOID?: string;
8
+ issuerDN: string;
9
+ serialNumber: any;
10
+ notAfter: string;
11
+ notBefore: string;
12
+ expired: boolean;
13
+ thumbprint: string;
14
+ commonName: string;
15
+ }
16
+ export declare interface JsonWebKey {
17
+ kty: string;
18
+ use: string;
19
+ kid: string;
20
+ alg: string;
21
+ n: string;
22
+ e: string;
23
+ }
24
+ export declare interface VerifySignatureResult {
25
+ valid: boolean;
26
+ certificates: Certificate[];
27
+ signatureProperties?: {
28
+ signingDate: string;
29
+ reason: string;
30
+ signingCertificate: Certificate;
31
+ };
32
+ }
33
+ export declare interface SignJsonSignatureResult {
34
+ payload: string;
35
+ signatures: {
36
+ protected: string;
37
+ signature: string;
38
+ }[];
39
+ }
40
+ export declare interface SignerServiceImpl {
41
+ getCerts(): Promise<Certificate[]>;
42
+ getJwks(): Promise<JsonWebKey[]>;
43
+ verify(context: ExpressDataContext, document: string, options?: {
44
+ filename: string;
45
+ contentType: string;
46
+ }): Promise<VerifySignatureResult[]>;
47
+ verifyFile(context: ExpressDataContext, file: string, options?: {
48
+ filename: string;
49
+ contentType: string;
50
+ }): Promise<VerifySignatureResult[]>;
51
+ signFile(context: ExpressDataContext, file: string, contentType?: string, extraOptions?: {
52
+ page?: number;
53
+ position?: number[];
54
+ timestampServer?: string;
55
+ reason?: string;
56
+ }): Promise<Buffer>;
57
+ sign(context: ExpressDataContext, data: unknown, extraOptions?: {
58
+ timestampServer?: string;
59
+ reason?: string;
60
+ }): Promise<SignJsonSignatureResult>;
61
+ }
@@ -1,39 +1,20 @@
1
1
  import { ApplicationBase, ApplicationService } from '@themost/common';
2
2
  import { ExpressDataContext } from '@themost/express';
3
- declare interface Certificate {
4
- version: number;
5
- subjectDN: string;
6
- sigAlgName?: string;
7
- sigAlgOID?: string;
8
- issuerDN: string;
9
- serialNumber: any;
10
- notAfter: string;
11
- notBefore: string;
12
- expired: boolean;
13
- thumbprint: string;
14
- commonName: string;
15
- }
16
- declare interface VerifySignatureResult {
17
- valid: boolean;
18
- certificates: Certificate[];
19
- signatureProperties?: {
20
- signingDate: string;
21
- reason: string;
22
- signingCertificate: Certificate;
23
- };
24
- }
3
+ import { VerifySignatureResult } from './SignerServiceImpl';
25
4
  export declare class SignerWorker extends ApplicationService {
5
+ protected options?: {
6
+ host?: string;
7
+ };
26
8
  constructor(app: ApplicationBase);
27
9
  /**
28
10
  *
29
11
  * @param {ExpressDataContext} context The underlying data context
30
12
  * @param {string} document The file path
31
13
  * @param {{filename: string, contentType: string}=} options Extra attributes (optional)
32
- * @returns
14
+ * @returns Promise<VerifySignatureResult[]>
33
15
  */
34
16
  verify(context: ExpressDataContext, document: string, options?: {
35
17
  filename: string;
36
18
  contentType: string;
37
19
  }): Promise<VerifySignatureResult[]>;
38
20
  }
39
- export {};
package/dist/index.d.ts CHANGED
@@ -1,2 +1,4 @@
1
1
  export * from './SignerWorker';
2
+ export * from './SignerServiceImpl';
2
3
  export * from './signerWorkerRouter';
4
+ export * from './SignerService';
package/dist/index.esm.js CHANGED
@@ -1,8 +1,16 @@
1
- import { HttpMethodNotAllowedError, ApplicationService, HttpError } from '@themost/common';
1
+ import { HttpMethodNotAllowedError, ApplicationService, HttpError, TraceUtils } from '@themost/common';
2
2
  import express from 'express';
3
3
  import { createProxyMiddleware } from 'http-proxy-middleware';
4
4
  import { serviceRouter } from '@themost/express';
5
5
  import request from 'superagent';
6
+ import { URL } from 'url';
7
+ import path from 'path';
8
+ import { writeFile as writeFile$1, unlink as unlink$1 } from 'fs';
9
+ import { promisify } from 'util';
10
+ import crypto from 'crypto';
11
+ import os from 'os';
12
+ import { DataCacheStrategy } from '@themost/data';
13
+ import { Secret, TOTP } from 'xotp';
6
14
 
7
15
  /*! *****************************************************************************
8
16
  Copyright (c) Microsoft Corporation.
@@ -29,16 +37,17 @@ function __awaiter(thisArg, _arguments, P, generator) {
29
37
  });
30
38
  }
31
39
 
32
- function signerWorkerRouter() {
40
+ function signerWorkerRouter(target = 'http://localhost:2465/') {
33
41
  const router = express.Router();
34
42
  router.use('/sign', (req, res, next) => {
35
43
  return next(new HttpMethodNotAllowedError('This method is not supported when using remote services.'));
36
44
  });
37
45
  router.use(createProxyMiddleware({
38
- target: 'http://localhost:2465/',
46
+ target,
39
47
  pathRewrite: {
40
48
  '^/api/signer': ''
41
49
  },
50
+ logLevel: 'warn',
42
51
  changeOrigin: true
43
52
  }));
44
53
  return router;
@@ -75,6 +84,10 @@ class ScopeAccessConfiguration {
75
84
  class SignerWorker extends ApplicationService {
76
85
  constructor(app) {
77
86
  super(app);
87
+ // get configuration
88
+ this.options = Object.assign({
89
+ host: 'http://localhost:2465/'
90
+ }, app.getConfiguration().getSourceAt('settings/universis/signerWorker'));
78
91
  const thisApp = app;
79
92
  if (thisApp && thisApp.container) {
80
93
  thisApp.container.subscribe((container) => {
@@ -85,7 +98,7 @@ class SignerWorker extends ApplicationService {
85
98
  scopeAccess.elements.push.apply(scopeAccess.elements, SignerScopeAccess);
86
99
  }
87
100
  // use signer worker router (for authenticated users)
88
- serviceRouter.use('/signer/', signerWorkerRouter());
101
+ serviceRouter.use('/signer/', signerWorkerRouter(this.options.host));
89
102
  }
90
103
  });
91
104
  }
@@ -95,19 +108,336 @@ class SignerWorker extends ApplicationService {
95
108
  * @param {ExpressDataContext} context The underlying data context
96
109
  * @param {string} document The file path
97
110
  * @param {{filename: string, contentType: string}=} options Extra attributes (optional)
98
- * @returns
111
+ * @returns Promise<VerifySignatureResult[]>
99
112
  */
100
113
  verify(context, document, options) {
101
114
  return __awaiter(this, void 0, void 0, function* () {
102
- const response = yield request.post('http://localhost:2465/verify')
103
- .attach('file', document, options);
104
- if (response.ok) {
105
- return response.body;
115
+ try {
116
+ const response = yield request.post(new URL('/verify', this.options.host).toString())
117
+ .attach('file', document, options);
118
+ if (response.ok) {
119
+ return response.body;
120
+ }
121
+ // noinspection ExceptionCaughtLocallyJS
122
+ throw new HttpError(response.statusCode);
123
+ }
124
+ catch (err) {
125
+ if (err.response) {
126
+ TraceUtils.error(err);
127
+ throw new HttpError(err.response.status);
128
+ }
129
+ throw err;
130
+ }
131
+ });
132
+ }
133
+ }
134
+
135
+ const writeFile = promisify(writeFile$1);
136
+ const unlink = promisify(unlink$1);
137
+ function tmpFile(extension = '.tmp') {
138
+ const tmpDir = os.tmpdir();
139
+ const filename = `file-${crypto.randomBytes(16).toString('hex')}${extension}`;
140
+ return path.join(tmpDir, filename);
141
+ }
142
+ class SignerService extends ApplicationService {
143
+ constructor(app) {
144
+ super(app);
145
+ // get signer worker configuration
146
+ const options = app.getConfiguration().getSourceAt('settings/universis/signerService') || {};
147
+ this.options = Object.assign({}, {
148
+ 'host': 'http://localhost:2465/'
149
+ }, options);
150
+ const thisApp = app;
151
+ if (thisApp && thisApp.container) {
152
+ thisApp.container.subscribe((container) => {
153
+ if (container) {
154
+ container.get('/api/signer/.well-known/jwks', (_req, res, next) => __awaiter(this, void 0, void 0, function* () {
155
+ try {
156
+ const results = yield this.getJwks();
157
+ return res.json(results);
158
+ }
159
+ catch (err) {
160
+ return next(err);
161
+ }
162
+ }));
163
+ container.get('/api/signer/.well-known/certs', (_req, res, next) => __awaiter(this, void 0, void 0, function* () {
164
+ try {
165
+ const results = yield this.getCerts();
166
+ return res.json(results);
167
+ }
168
+ catch (err) {
169
+ return next(err);
170
+ }
171
+ }));
172
+ }
173
+ });
174
+ }
175
+ }
176
+ /**
177
+ * @param {ExpressDataContext} context The underlying data context
178
+ * @param {*} data The data to be verified
179
+ * @param {{filename: string, contentType: string}=} options Extra attributes (optional)
180
+ * @returns {Promise<VerifySignatureResult[]>}
181
+ */
182
+ verify(context, data, options) {
183
+ return __awaiter(this, void 0, void 0, function* () {
184
+ if (data == null) {
185
+ throw new Error('The given data cannot be empty during sign.');
186
+ }
187
+ const json = (typeof data === 'object') ? JSON.stringify(data) : JSON.stringify({
188
+ value: data,
189
+ });
190
+ const file = tmpFile('.json');
191
+ // write data to temporary file
192
+ yield writeFile(file, json, { encoding: 'utf8' });
193
+ try {
194
+ return yield this.verifyFile(context, file, options);
195
+ }
196
+ catch (err) {
197
+ throw err;
198
+ }
199
+ finally {
200
+ try {
201
+ yield unlink(file); // remove temporary file
202
+ }
203
+ catch (e) {
204
+ // ignore error
205
+ }
206
+ }
207
+ });
208
+ }
209
+ getJwks() {
210
+ return __awaiter(this, void 0, void 0, function* () {
211
+ try {
212
+ const cache = this.getApplication().getConfiguration().getStrategy(DataCacheStrategy);
213
+ // check if cache is available
214
+ return cache.getOrDefault('/api/signer/keystore/jwks', () => __awaiter(this, void 0, void 0, function* () {
215
+ try {
216
+ const response = yield request
217
+ .get(new URL('/keystore/jwks', this.options.host).toString())
218
+ .auth(this.options.user, this.options.password);
219
+ if (response.ok) {
220
+ return response.body;
221
+ }
222
+ // noinspection ExceptionCaughtLocallyJS
223
+ throw new HttpError(response.status, 'Something went wrong while getting json web key set');
224
+ }
225
+ catch (err) {
226
+ TraceUtils.error(err);
227
+ throw new HttpError(err.status, 'Failed to get json web key set', err.message);
228
+ }
229
+ }));
230
+ }
231
+ catch (err) {
232
+ throw new HttpError(err.status, 'Failed to get json web key set', err.message);
233
+ }
234
+ });
235
+ }
236
+ getCerts() {
237
+ return __awaiter(this, void 0, void 0, function* () {
238
+ const cache = this.getApplication().getConfiguration().getStrategy(DataCacheStrategy);
239
+ return cache.getOrDefault('/api/signer/keystore/certs', () => __awaiter(this, void 0, void 0, function* () {
240
+ try {
241
+ const response = yield request.get(new URL('/keystore/certs', this.options.host).toString())
242
+ .auth(this.options.user, this.options.password);
243
+ if (response.ok) {
244
+ return response.body.filter((item) => {
245
+ return item.notAfter && new Date(item.notAfter) > new Date();
246
+ });
247
+ }
248
+ return [];
249
+ }
250
+ catch (err) {
251
+ TraceUtils.error(err);
252
+ throw new HttpError(err.status, 'Failed to get certificates', err.message);
253
+ }
254
+ }));
255
+ });
256
+ }
257
+ /**
258
+ * Generates a one-time password using the configured secret.
259
+ */
260
+ generateVerificationCode(options) {
261
+ if (this.options.secret == null) {
262
+ throw new Error('One-time password is not supported');
263
+ }
264
+ const secret = Secret.from(this.options.secret, 'base32');
265
+ const opts = Object.assign({
266
+ algorithm: 'sha1',
267
+ digits: 6,
268
+ period: 30,
269
+ }, options);
270
+ const totp = new TOTP(opts);
271
+ return totp.generate({ secret });
272
+ }
273
+ /**
274
+ * @param {ExpressDataContext} context The underlying data context
275
+ * @param {*} data Any JSON object to be signed
276
+ * @param {{timestampServer?:string}} extraOptions
277
+ * @returns {Promise<SignJsonSignatureResult>} An array of signatures
278
+ */
279
+ sign(context, data, extraOptions) {
280
+ return __awaiter(this, void 0, void 0, function* () {
281
+ if (data == null) {
282
+ throw new Error('The given data cannot be empty during sign.');
283
+ }
284
+ const json = (typeof data === 'object') ? JSON.stringify(data) : JSON.stringify({
285
+ value: data,
286
+ });
287
+ const file = tmpFile('.json');
288
+ // write data to temporary file
289
+ yield writeFile(file, json, { encoding: 'utf8' });
290
+ try {
291
+ return yield this.signFinal(context, file, 'application/json', extraOptions);
292
+ }
293
+ catch (err) {
294
+ if (err.response) {
295
+ throw new HttpError(err.status, 'Failed to sign data', err.message);
296
+ }
297
+ throw err;
298
+ }
299
+ finally {
300
+ try {
301
+ yield unlink(file); // remove temporary file
302
+ }
303
+ catch (e) {
304
+ // ignore error
305
+ }
306
+ }
307
+ });
308
+ }
309
+ signFile(context, file, contentType, extraOptions) {
310
+ return __awaiter(this, void 0, void 0, function* () {
311
+ return this.signFinal(context, file, contentType, extraOptions);
312
+ });
313
+ }
314
+ verifyFile(context, file, options) {
315
+ return __awaiter(this, void 0, void 0, function* () {
316
+ try {
317
+ const response = yield request.post(new URL('/verify', this.options.host).toString())
318
+ .attach('file', file, options);
319
+ if (response.ok) {
320
+ return response.body;
321
+ }
322
+ // noinspection ExceptionCaughtLocallyJS
323
+ throw new HttpError(response.statusCode);
324
+ }
325
+ catch (err) {
326
+ if (err.response) {
327
+ TraceUtils.error(err);
328
+ throw new HttpError(err.response.status, 'Failed to verify data', err.message);
329
+ }
330
+ throw err;
331
+ }
332
+ });
333
+ }
334
+ inspect(context, file, options) {
335
+ return __awaiter(this, void 0, void 0, function* () {
336
+ try {
337
+ const fields = {};
338
+ if (this.options.inspector) {
339
+ Object.assign(fields, this.options.inspector);
340
+ }
341
+ const response = yield request.post(new URL('/signature/inspect', this.options.host).toString())
342
+ .field(fields)
343
+ .attach('file', file, options);
344
+ if (response.ok) {
345
+ return response.body;
346
+ }
347
+ // noinspection ExceptionCaughtLocallyJS
348
+ throw new HttpError(response.statusCode);
349
+ }
350
+ catch (err) {
351
+ if (err.response) {
352
+ TraceUtils.error(err);
353
+ throw new HttpError(err.response.status, 'Failed to verify data', err.message);
354
+ }
355
+ throw err;
356
+ }
357
+ });
358
+ }
359
+ requiresVerificationCode() {
360
+ return typeof this.options.secret === 'string' && this.options.secret.length > 0;
361
+ }
362
+ signFinal(context, file, contentType, extraOptions) {
363
+ return __awaiter(this, void 0, void 0, function* () {
364
+ // try to find thumbprint if empty
365
+ if (typeof this.options.thumbprint === 'undefined') {
366
+ const [cert] = yield this.getCerts();
367
+ if (cert) {
368
+ this.options.thumbprint = cert.thumbprint;
369
+ }
370
+ else {
371
+ this.options.thumbprint = '';
372
+ }
373
+ }
374
+ const fields = Object.assign({}, extraOptions);
375
+ // if the file is a PDF or the content type is application/pdf, try to inspect signature position
376
+ if (path.extname(file) === '.pdf' || contentType === 'application/pdf') {
377
+ if ((extraOptions === null || extraOptions === void 0 ? void 0 : extraOptions.page) && (extraOptions === null || extraOptions === void 0 ? void 0 : extraOptions.position)) {
378
+ // if page and position are defined, use them
379
+ Object.assign(fields, {
380
+ page: extraOptions.page,
381
+ position: extraOptions.position.map((element) => {
382
+ return Math.round(element);
383
+ }).join(',')
384
+ });
385
+ }
386
+ else if (this.options.inspector) {
387
+ // if inspector is defined, use it
388
+ const inspectorResult = yield this.inspect(context, file);
389
+ if ((inspectorResult === null || inspectorResult === void 0 ? void 0 : inspectorResult.page) && (inspectorResult === null || inspectorResult === void 0 ? void 0 : inspectorResult.position)) {
390
+ Object.assign(fields, {
391
+ page: inspectorResult.page,
392
+ position: inspectorResult.position.map((element) => {
393
+ return Math.round(element);
394
+ }).join(',')
395
+ });
396
+ }
397
+ }
398
+ }
399
+ if (this.options.thumbprint.length) {
400
+ Object.assign(fields, {
401
+ thumbprint: this.options.thumbprint,
402
+ });
403
+ }
404
+ const fileOptions = {
405
+ filename: path.basename(file)
406
+ };
407
+ if (contentType) {
408
+ Object.assign(fileOptions, {
409
+ contentType
410
+ });
411
+ }
412
+ try {
413
+ // if secret is defined, generate one-time password
414
+ if (this.requiresVerificationCode()) {
415
+ Object.assign(fields, {
416
+ otp: this.generateVerificationCode()
417
+ });
418
+ }
419
+ const response = yield request.post(new URL('/sign', this.options.host).toString())
420
+ .type('form')
421
+ .auth(this.options.user, this.options.password)
422
+ .field(fields)
423
+ .buffer(contentType !== 'application/json')
424
+ .attach('file', file, fileOptions);
425
+ if (response.ok) {
426
+ return response.body;
427
+ }
428
+ // noinspection ExceptionCaughtLocallyJS
429
+ throw new HttpError(response.status);
430
+ }
431
+ catch (err) {
432
+ if (err.response) {
433
+ TraceUtils.error(err);
434
+ throw new HttpError(err.response.status, 'Failed to sign data', err.message);
435
+ }
436
+ throw err;
106
437
  }
107
- throw new HttpError(response.statusCode);
108
438
  });
109
439
  }
110
440
  }
111
441
 
112
- export { SignerWorker, signerWorkerRouter };
442
+ export { SignerService, SignerWorker, signerWorkerRouter };
113
443
  //# sourceMappingURL=index.esm.js.map