diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..5f9ce88 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true +max_line_length = 120 + +[*.md] +trim_trailing_whitespace = false diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..db2cc34 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,3 @@ +bin +node_modules +dist diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..100bc2f --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,24 @@ +module.exports = { + root: true, + parser: "@typescript-eslint/parser", + plugins: [ + "@typescript-eslint", + ], + extends: [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended", + ], + rules: { + "quotes": "error", + "semi": "off", + "key-spacing": ["error", { "align": "value" }], + "comma-dangle": ["error", "always-multiline"], + "object-curly-spacing": ["error", "always"], + "array-bracket-spacing": "error", + "indent": "off", + "@typescript-eslint/indent": ["error", 2], + "@typescript-eslint/semi": ["error"], + "@typescript-eslint/no-explicit-any": "off", + }, +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..49cbbf8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +node_modules +.vscode +/web-eid.js +/config.js +/errors +/models +/services +/utils +/*.tgz +dist/ +*.swp diff --git a/LICENSE b/LICENSE index b5f78ce..0b8eeef 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 eID on platform Web +Copyright (c) 2020 The Web eID Project Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md new file mode 100644 index 0000000..974ab1d --- /dev/null +++ b/README.md @@ -0,0 +1,1053 @@ +# web-eid.js + +![European Regional Development Fund](https://github.com/e-gov/RIHA-Frontend/raw/master/logo/EU/EU.png) + +`web-eid.js` — add strong authentication and digital signing with electronic ID smart cards to +web applications with the Web eID JavaScript library. `web-eid.js` +is a thin wrapper on top of the messaging interface provided by the Web eID +browser extension. + +## Quickstart + +Complete the three steps below to add strong authentication and digital signing +support to your web application front end. Instructions for the back end are +available [here](). + +To run this quickstart you need a modern web application that uses NPM to +manage JavaScript packages. + +See full example [here](). + +### 1. Add web-eid.js to the project + +1. Run the following command to install the library using NPM: + + echo '@web-eid:registry=https://gitlab.com/api/v4/packages/npm' >> .npmrc + npm install web-eid + +1. Configure the web server to expose `node_modules/web-eid/dist/es/web-eid.js` + +See below for alternative installation options. + +### 2. Add authentication support to your web app + +Copy the following code to the authentication page of your web application, +amending `options` according to your back end configuration: + +```html +

+ +

+ + +``` + +See below for `options` description. + +### 3. Add digital signature support to your web app + +Copy the following code to the digital signing page of your web application, +amending `options` according to your back end configuration: + +```html +

+ +

+ + +``` + +See below for `options` description. + +### 4. Run the example + +1. Download and run [the Web eID installer](https://web-eid.eu) for your operating system. +2. Start your web appliction. +3. Web eID requires HTTPS. If your web application does not have HTTPS support, use e.g. [ngrok](https://ngrok.com/) to add it. +4. Open the HTTPS URL in the browser, attach a smart card reader, insert the electronic ID card and try out authentication and signing. + +## Table of contents + + + + + +- [Quickstart](#quickstart) +- [Install](#install) + - [Without a module system](#without-a-module-system) + - [ES module for server-side build tools](#es-module-for-server-side-build-tools) + - [ES module for browsers](#es-module-for-browsers) + - [UMD module for AMD and browser-side CommonJS module loaders](#umd-module-for-amd-and-browser-side-commonjs-module-loaders) +- [API](#api) + - [Status](#status) + - [Status parameters](#status-parameters) + - [Status returns](#status-returns) + - [Status example - check using async-await](#status-example-check-using-async-await) + - [Status example - check using promises](#status-example-check-using-promises) + - [Status example - success](#status-example-success) + - [Status example - failure](#status-example-failure) + - [Authenticate](#authenticate) + - [Authenticate parameters](#authenticate-parameters) + - [Authenticate returns](#authenticate-returns) + - [Authenticate example - using async-await](#authenticate-example-using-async-await) + - [Authenticate example - using promises](#authenticate-example-using-promises) + - [Authenticate example - success](#authenticate-example-success) + - [Authenticate example - failure](#authenticate-example-failure) + - [Sign](#sign) + - [Sign parameters](#sign-parameters) + - [Sign returns](#sign-returns) + - [Sign example - using async-await](#sign-example-using-async-await) + - [Sign example - using promises](#sign-example-using-promises) + - [Sign example - success](#sign-example-success) + - [Sign example - failure](#sign-example-failure) +- [Known errors](#known-errors) + - [Error codes](#error-codes) + - [Timeout errors](#timeout-errors) + - [Health errors](#health-errors) + - [Security errors](#security-errors) + - [Third party errors](#third-party-errors) + - [Developer mistakes](#developer-mistakes) +- [Development](#development) + - [Testing changes locally](#testing-changes-locally) + - [Using `npm link`](#using-npm-link) + - [Using `npm pack`](#using-npm-pack) + + + + +## Install + +### Without a module system + +Use the **dist/iife** build. It exposes `webeid` object globally. +```html + + +``` + + +### ES module for server-side build tools + +When using a server-side build tool like WebPack, Babel, Rollup or the +TypeScript compiler: + +1. Run the following command to install the library using NPM: + + echo '@web-eid:registry=https://gitlab.com/api/v4/packages/npm' >> .npmrc + npm install web-eid + +2. Import modules without an extension: + +```javascript +// Import the Web-eID library +import webeid from 'web-eid'; + +// ...or only what you need +import { + status, + authenticate, + Action, + ErrorCode, + hasVersionProperties +} from 'web-eid'; + + +// If you need TypeScript interfaces, they are also available! +import AuthenticateOptions from 'web-eid/models/AuthenticateOptions'; +``` + +### ES module for browsers + +1. Configure the web server to expose `node_modules/web-eid/dist/es/web-eid.js` +2. Import the modules and specify the file extension + +```javascript +// Import the Web-eID library +import webeid from 'web-eid.js'; +``` + +### UMD module for AMD and browser-side CommonJS module loaders +Use the **dist/umd** build. + +**CommonJS** +```javascript +var webeid = require("web-eid"); +``` + +**AMD** +```javascript +define(["web-eid"], function(webeid) { + ... +}); +``` + + +## API + +### Status +```ts +status(options?: RequestOptions): Promise +``` + +To verify that the user has a valid extension and native application version, a status check can be performed. +We recommend checking the status before performing other actions - if status check fails, other actions will most likely fail as well. +However the choice is yours and you may perform the status check retroactively when an action (for example [authenticate](#authenticate)) fails. + +#### Status parameters + +| Name | Type | Default | Description | +|-------------------|----------|---------|-----------------------------------------| +| `options` | `object` | | Optional status request options object | +| `options.timeout` | `number` | `2000` | Timeout for the request in milliseconds | + +#### Status returns + +```ts +Promise +``` +```typescript +Version { + // SemVer string in the format x.y.z + library: string; + + // SemVer string in the format x.y.z + nativeApp: string; + + // SemVer string in the format x.y.z + extension: string; +} +``` + +#### Status example - check using async-await + +```js +try { + const status = await webeid.status(); + + // HANDLE SUCCESS + +} catch (error) { + + // HANDLE FAILURE + +} +``` + +#### Status example - check using promises + +```js +webeid.status() +.then((status) => { /* HANDLE SUCCESS */ }) +.catch((error) => { /* HANDLE FAILURE */ }); +``` + +#### Status example - success + +The result of a status check is the status object which contains SemVer strings for the library, browser extension and native application. + +```js +{ + library: "1.2.3", + nativeApp: "1.2.3", + extension: "1.2.3" +} +``` + +#### Status example - failure + +When the status check fails, in addition to the usual `name`, `message` and `stack` properties, the error object contains additional info **when possible**. +See [Known errors](#known-errors) for error `code` options. + +```javascript +{ + // Name of the Error object + "name": "VersionMismatchError", + + // Human readable message, meant for the developer + "message": "Update required for Web-eID extension and native app", + + // Programmatic error code, meant for error handling + "code": "ERR_WEBEID_VERSION_MISMATCH", + + // SemVer strings of the library, always present + "library": "1.2.3", + + // Missing if extension is not installed + "extension": "0.2.3", + + // Missing if extension or native application is not installed + "nativeApp": "0.2.3", + + // List of components which require an update + // Only present if the error is ERR_WEBEID_VERSION_MISMATCH + "requiresUpdate": { + "extension": true, + "nativeApp": true + }, + + // Stack trace + "stack": ... +} +``` + + +### Authenticate + +```ts +authenticate(options: AuthenticateOptions): Promise +``` +Requests the Web-eID browser extension to authenticate the user. + +#### Authenticate parameters + +| Name | Type | Default | Description | +|----------------------------------|----------|----------|-------------------------------------------------------| +| `options` | `object` | | **Required** authentication request options object | +| `options.getAuthChallengeUrl` | `string` | | **Required** authentication challenge GET request URL | +| `options.postAuthTokenUrl` | `string` | | **Required** authentication token POST request URL | +| `options.headers` | `object` | `{ }` | **Optional** HTTP request headers | +| `options.userInteractionTimeout` | `number` | `120000` | **Optional** user interaction timeout in milliseconds | +| `options.serverRequestTimeout` | `number` | `20000` | **Optional** server request timeout in milliseconds | + +**`AuthenticateOptions.getAuthChallengeUrl`** +This URL should respond to a GET request with a cryptographic nonce. +The request will be made by the browser extension. +The extension will use this nonce to generate an authentication token. + +**`AuthenticateOptions.postAuthTokenUrl`** +This URL should accept a JSON payload via a POST request which contains +the authentication token generated by the browser extension. + +After the server validates the token, +the server should respond with a 2xx status code and an optional JSON payload. + +If the provided token was invalid or the user should be forbidden access +for a different reason, the server should respond with an appropriate +HTTP error status code and an optional JSON payload. + +When this request succeeds or fails, the response, including the optional payload, will be part of the resolution or failure of the Promise which the `authenticate(...)` method returns. + +**`AuthenticateOptions.headers`** +This optional field may contain additional HTTP headers which the browser extension will use while making the **auth challenge** and **auth token** requests. +For example, this option can be used to specify authorization headers. + +#### Authenticate returns + +```ts +Promise // HTTP response of the auth token POST request +``` +```typescript +interface HttpResponse { + // The HTTP request's response headers. + headers: Iterable<[string, string]>; + + // A boolean indicating whether the response + ok: boolean; + + // Indicates whether or not the response is the result of a redirect. + redirected: boolean; + + // The status code of the response. (This will be 200 for a success). + status: number; + + // The status message corresponding to the status code. (e.g., OK for 200). + statusText: string; + + // The type of the response (e.g., basic, cors). + type: string; + + // The URL of the response. + url: string; + + // Response body. Can be an object deserialized from JSON or plain text. + body: object | string; +} +``` + +#### Authenticate example - using async-await + +```js +try { + const options = { + getAuthChallengeUrl: "/auth-challenge", + postAuthTokenUrl: "/auth-token", + }; + + const response = await webeid.authenticate(options); + + // HANDLE SUCCESS + +} catch (error) { + + // HANDLE FAILURE + +} +``` + +#### Authenticate example - using promises + +```js +const options = { + getAuthChallengeUrl: "/auth/challenge", + postAuthTokenUrl: "/auth/token", +}; + +webeid.authenticate(options) +.then((status) => { /* HANDLE SUCCESS */ }) +.catch((error) => { /* HANDLE FAILURE */ }); +``` + +#### Authenticate example - success + +The result of a status check is the status object which contains SemVer strings for the library, browser extension and native application. + +```js +{ + // The body content is useful when authenticating in a SPA applications. + // The server can also send a 204 status with an empty response. + body: { + name: { + first: "John", + last: "Smith", + }, + age: 20, + }, + + headers: { + "connection": "keep-alive", + "content-length": "49", + "content-type": "application/json; charset=utf-8", + "date": "Mon, 27 Apr 2020 06:28:54 GMT", + "etag": "W/\"30-YHV2nUGU912eoDvI+roJ2Yqn5SA\"", + "x-powered-by": "Express" + } + + ok: true, + redirected: false, + status: 200, + statusText: "OK", + type: "basic", + + // The server may chooses to redirect the request on the + // postAuthTokenUrl address. In case of one or more redirects + // the URL will be set to the last URL which finally gave an OK status. + url: "https://example.com/auth/token", +} +``` + +#### Authenticate example - failure + +When the authenticate request fails, in addition to the usual `name`, `message` and `stack` properties, the error object contains additional info **when possible**. +See [Known errors](#known-errors) for error `code` options. + +```js +{ + // Name of the Error object + name: "ServerRejectedError", + + // Human readable message, meant for the developer + message: "server rejected the request", + + // Programmatic error code, meant for error handling + code: "ERR_WEBEID_SERVER_REJECTED", + + // The response object only exists when the server responded + // with a failure status code. + response: { + body: { + message: "Invalid token", + }, + + headers: { + "connection": "keep-alive", + "content-length": "25", + "content-type": "application/json; charset=utf-8", + "date": "Mon, 27 Apr 2020 06:28:54 GMT", + "etag": "W/\"30-YHV2nUGU912eoDvI+roJ2Yqn5SA\"", + "x-powered-by": "Express", + }, + + ok: false, + redirected: false, + status: 401, + statusText: "Unauthorized", + type: "basic", + + url: "https://example.com/auth/token", + }, + + // Stack trace + "stack": ... +} +``` + + +### Sign + +```ts +sign(options: SignOptions): Promise +``` +Requests the Web-eID browser extension to sign a document hash. + +#### Sign parameters + +| Name | Type | Default | Description | +|----------------------------------|----------|----------|-------------------------------------------------------| +| `options` | `object` | | **Required** sign request options object | +| `options.postPrepareSigningUrl` | `string` | | **Required** prepare signing POST request URL | +| `options.postFinalizeSigningUrl` | `string` | | **Required** finalize signing POST request URL | +| `options.headers` | `object` | `{ }` | **Optional** HTTP request headers | +| `options.userInteractionTimeout` | `number` | `120000` | **Optional** user interaction timeout in milliseconds | +| `options.serverRequestTimeout` | `number` | `20000` | **Optional** server request timeout in milliseconds | + +**`SignOptions.postPrepareSigningUrl`** +During the signing process, a POST request will be made by the browser extension against the backend service using this URL to initiate preparations for document signing. + +The request body will have a JSON payload with `certificate` and `supportedSignatureAlgorithms` field which is provided by the browser extension. + +*Example request body:* +```js +{ + // User's X.509 signing certificate + certificate: "MIIF2DCCA8CgAwIBAgIQVcAUfV...", + + supportedSignatureAlgorithms: [ + { + crypto: "ECC", + hash: "SHA-224", + padding: "NONE" + }, + { + crypto: "ECC", + hash: "SHA-256", + padding: "NONE" + }, + { + crypto: "ECC", + hash: "SHA-384", + padding: "NONE" + }, + ... + ] +} +``` + +Additional information about the request, for example the ID of the document to be signed, can be passed to the server via URL parameters. +Furthermore, additional request headers, for example authentication and authorization tokens, can be specified using `SignOptions.headers`. + +The response must be a JSON object with `hash` and `algorithm` fields. +Additional fields can be specified in the response, these fields will be passed on to the **finalize document** request by the browser extension. + +*Example response body:* +```js +{ + // Base64-encoded document hash + hash: "iH7aF+AIAml46AgI2oYGcJzYzbg9JYFKR0oniGAZ7CkT8zVMx9JZtIelHezaOKyP", + + // Hash algorithm from the supportedSignatureAlgorithms array + algorithm: "SHA-384", + + // Optional arbitrary data for the finalize request + documentId: 123, + expires: "2020-10-20T00:00:00.000+0300" +} +``` + +**`SignOptions.postFinalizeSigningUrl`** +The browser extension will use this URL to make a POST request for which the backend service should finalize the signing process. + +The request body will have a JSON payload with the `signature` field. In addition, the payload includes the `hash`, `algorithm` and additional arbitrary fields provided by the server during the **prepare signing** request. + +*Example request body:* +```js +{ + // Base64-encoded signature + signature: "O0vhA3XSflWsE/v0xcdLGPG0mbWHySSPXWJkRni8vklWKhlzWvGuHD98rWZzf31VsuldBlhJo9eflZvmKK/tUuTjiwXw2BLq3E+qv6Vs6nLHJNJs/ki6Lm/s+bwffyrH", + + // Base64-encoded document hash from the prepare request's response + hash: "SHA-384", + + // Hash algorithm from the prepare request's response + algorithm: "", + + // Optional arbitrary data from the prepare request's response + documentId: 123, + expires: "2020-10-20T00:00:00.000+0300" +} +``` + +When this request succeeds or fails, the response, including the optional payload, will be part of the resolution or failure of the Promise which the `sign(...)` method returns. + +**`SignOptions.headers`** +This optional field may contain additional HTTP headers which the browser extension will use while making the **prepare** and **finalize** requests. +For example, this option can be used to specify authorization headers. + + +#### Sign returns + +```ts +Promise // HTTP response of the finalize signing POST request +``` +```typescript +interface HttpResponse { + // The HTTP request's response headers. + headers: Iterable<[string, string]>; + + // A boolean indicating whether the response + ok: boolean; + + // Indicates whether or not the response is the result of a redirect. + redirected: boolean; + + // The status code of the response. (This will be 200 for a success). + status: number; + + // The status message corresponding to the status code. (e.g., OK for 200). + statusText: string; + + // The type of the response (e.g., basic, cors). + type: string; + + // The URL of the response. + url: string; + + // Response body. Can be an object deserialized from JSON or plain text. + body: object | string; +} +``` + +#### Sign example - using async-await + +```js +try { + const options = { + postPrepareSigningUrl: "/document/123/sign/prepare", + postFinalizeSigningUrl: "/document/123/sign/finalize", + }; + + const response = await webeid.sign(options); + + // HANDLE SUCCESS + +} catch (error) { + + // HANDLE FAILURE + +} +``` + +#### Sign example - using promises + +```js +const options = { + postPrepareSigningUrl: "/document/123/sign/prepare", + postFinalizeSigningUrl: "/document/123/sign/finalize", +}; + +webeid.sign(options) +.then((status) => { /* HANDLE SUCCESS */ }) +.catch((error) => { /* HANDLE FAILURE */ }); +``` + +#### Sign example - success + +The result of a status check is the status object which contains SemVer strings for the library, browser extension and native application. + +```js +{ + // The body content is useful when signing in a SPA applications. + // The server can also send a 204 status with an empty response. + body: { + documentId: "123" + }, + + headers: { + "content-length": "20", + "content-type": "application/json; charset=utf-8", + "date": "Thu, 10 Sep 2020 18:28:12 GMT", + "etag": "W/\"14-jy7dMclQhztcZaNKkjCj7ZOnAXw\"", + "x-firefox-spdy": "h2", + "x-powered-by": "Express" + }, + + ok: true, + redirected: false, + status: 200, + statusText: "OK", + type: "basic", + + // The server may chooses to redirect the request on the + // postFinalizeSigningUrl address. In case of one or more redirects + // the URL will be set to the last URL which finally gave an OK status. + url: "https://example.com/document/123/sign/finalize" +} +``` + +#### Sign example - failure + +When the signing request fails, in addition to the usual `name`, `message` and `stack` properties, the error object contains additional info **when possible**. +See [Known errors](#known-errors) for error `code` options. + +```js +{ + // Name of the Error object + name: "ServerRejectedError" + + // Human readable message, meant for the developer + message: "server rejected the request" + + // Programmatic error code, meant for error handling + code: "ERR_WEBEID_SERVER_REJECTED" + + // The response object only exists when the server responded + // with a failure status code. + response: { + body: { + message: "Prepared document hash doesn't match" + }, + + headers: { + "content-length": "50", + "content-type": "application/json; charset=utf-8", + "date": "Thu, 10 Sep 2020 18:34:26 GMT", + "etag": "W/\"32-irPE0oVZ8Habt+b+xMX5wvxwCkk\"", + "x-firefox-spdy": "h2", + "x-powered-by": "Express" + }, + + ok: false, + redirected: false, + status: 400, + statusText: "Bad Request", + type: "basic", + + url: "https://example.com/document/234/sign/finalize" + } + + // Stack trace + "stack": ... +} +``` + +## Known errors + +There are several known errors that you can catch for the purpose of displaying more helpful error messages to the user. +Errors returned by the library should always have a `code` property which contains a programmatic error code. + +To avoid typos, you can use the `ErrorCode` enum. + +**Example** +```ts +try { + const response = await webeid.authenticate(options); + + // HANDLE SUCCESS + +} catch (error) { + switch (error.code) { + case webeid.ErrorCode.ERR_WEBEID_SERVER_REJECTED: { + // It's possible to check the response status code or body here + if (error.response?.status === 401) { + showError(error.response.body.message); + + } else if (error.response?.status === 503) + showError( + "Authentication is disabled while we are " + + "performing routine maintenance, try again in 10 minutes!" + ); + + } else { + showError( + "Authentication failed, " + + "please check your credentials and try again!" + ); + + } + break; + } + + case webeid.ErrorCode.ERR_WEBEID_USER_TIMEOUT: { + showError("Authentication timed out, please try again!"); + break; + } + + default: { + showError( + "An unknown error occurred. " + + "Please try again and contact support if the problem persists!" + ) + } + } +} +``` + +#### Error codes + +##### Timeout errors + +- **`ERR_WEBEID_ACTION_TIMEOUT`** + - **Thrown when:** The browser extension has accepted a task, replying to an action with an acknowledge message, but failed to reply reply with a success or failure message in time. + - **Likely reason:** Should not happen. + - **How to resolve:** Report a bug. + +- **`ERR_WEBEID_USER_TIMEOUT`** + - **Thrown when:** User interaction timed out. + - **Likely reason:** User failed to enter a PIN code. In addition, the user did not cancel PIN entry. + - **How to resolve:** The user may try again. The user interaction timeout is configurable, see [Authenticate](#authenticate) and [Sign](#sign) for details. + +- **`ERR_WEBEID_SERVER_TIMEOUT`** + - **Thrown when:** Server request timed out. + - **Likely reason:** Server failed to respond in a given time. + - **How to resolve:** The server may be busy. The server request timeout is configurable, see [Authenticate](#authenticate) and [Sign](#sign) for details. + +##### Health errors + +- **`ERR_WEBEID_VERSION_MISMATCH`** + - **Thrown when:** The browser extension and/or native application versions are not matching. + - **Likely reason:** Either the browser extension or the native application or both, require an update + - **How to resolve:** An additional object `requiresUpdate` might be attached to the error object. + The user can be instructed to update the required component. + + ```js + try { + const status = webeid.status(); + ... + } catch (error) { + if (error?.code === "ERR_WEBEID_VERSION_MISMATCH") { + if (error.requiresUpdate?.extension) { + // Web eID browser extension needs to be updated + } + + if (error.requiresUpdate?.nativeApp) { + // Web eID native application needs to be updated + } + } + } + ``` + +- **`ERR_WEBEID_VERSION_INVALID`** + - **Thrown when:** The native application did not provide a valid version string during handshake. + - **Likely reason:** Should not happen. + - **How to resolve:** Report a bug. + +- **`ERR_WEBEID_EXTENSION_UNAVAILABLE`** + - **Thrown when:** The browser extension fails during the handshake step and does not respond to an action message with a matching acknowledge message. + - **Likely reason:** The browser extension is not installed. + - **How to resolve:** The user can be instructed to install the Web eID browser extension. + +- **`ERR_WEBEID_NATIVE_UNAVAILABLE`** + - **Thrown when:** The native application fails during the handshake step. + - **Likely reason:** The browser extension is not installed. + - **How to resolve:** The user can be instructed to install the Web eID native application. + +- **`ERR_WEBEID_UNKNOWN_ERROR`** + - **Thrown when:** An unknown error occurs. + - **Likely reason:** Should not happen. + - **How to resolve:** Report a bug. + +##### Security errors +- **`ERR_WEBEID_CONTEXT_INSECURE`** + - **Thrown when:** The library is used from an insecure context. + - **Likely reason:** See [https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts) + - **How to resolve:** Use HTTPS. + +- **`ERR_WEBEID_PROTOCOL_INSECURE`** + - **Thrown when:** A provided URL does not start with `https:`. + - **Likely reason:** getAuthChallengeUrl or postAuthTokenUrl options are missing a secure protocol. + - **How to resolve:** Use HTTPS. + +- **`ERR_WEBEID_TLS_CONNECTION_BROKEN`** + - **Thrown when:** There's an issue with the TLS connection. + - **Likely reason:** The TLS handshake failed (for example, the certificate had expired) + - **How to resolve:** Check the website certificate. + +- **`ERR_WEBEID_TLS_CONNECTION_INSECURE`** + - **Thrown when:** There's an issue with the TLS connection. + - **Likely reason:** The connection is not a TLS connection. + - **How to resolve:** Check the website certificate. + +- **`ERR_WEBEID_TLS_CONNECTION_WEAK`** + - **Thrown when:** There's an issue with the TLS connection. + - **Likely reason:** The connection is a TLS connection but is considered weak. Most likely the negotiated cipher suite is considered weak. + - **How to resolve:** Check the website certificate. + +- **`ERR_WEBEID_CERTIFICATE_CHANGED`** + - **Thrown when:** Certificate fingerprint changed between requests during the same operation. + - **Likely reason:** Either a man-in-the-middle attack attempt or the website changed certificate between requests. + - **How to resolve:** Log the incident and instruct the user to try again. + +- **`ERR_WEBEID_ORIGIN_MISMATCH`** + - **Thrown when:** Origins of provide URLs mismatch for an operation. + - **Likely reason:** The URLs provided in `authenticate` or `sign` method options were not using the same origin (protocol, domain and port). Either a developer mistake or an XSS attack attempt. + - **How to resolve:** Make sure the same origin is used. Log the incident and instruct the user to try again. + +##### Third party errors +- **`ERR_WEBEID_SERVER_REJECTED`** + - **Thrown when:** The server replies, but with an HTTP error status code (400 or greater). + - **Likely reason:** User not authorized, server rejects document signing or other server error. + - **How to resolve:** This error should include the `response` object that might provide the necessary details. + + ```js + try { + const response = webeid.authenticate(options); + ... + } catch (error) { + if (error?.code === "ERR_WEBEID_SERVER_REJECTED") { + if (error.response?.status === 401) { + // Authorization has been refused + } + } + } + ``` + +- **`ERR_WEBEID_USER_CANCELLED`** + - **Thrown when:** User cancelled the current operation. + - **Likely reason:** User closed the certificate selection, pin entry or similar user interaction dialog. + - **How to resolve:** Respect the user's wishes. + +- **`ERR_WEBEID_NATIVE_FATAL`** + - **Thrown when:** Native application terminated with a fatal error. + - **Likely reason:** The native application was in an unrecoverable state. For example, the card reader might have been malfunctioning. + - **How to resolve:** Log the incident and instruct the user to try again. Inspect the error object's `nativeException` attribute for details. + +##### Developer mistakes +- **`ERR_WEBEID_ACTION_PENDING`** + - **Thrown when:** An action of the same type is already pending. + - **Likely reason:** The user is allowed to initiate actions multiple times without waiting for their resolution. + - **How to resolve:** Make sure to disable buttons which trigger actions until the action is resolved. + + ```js + authenticateButton.disabled = true; + + try { + const response = await authenticate(options); + ... + } catch (error) { + ... + } finally { + authenticateButton.disabled = false; + } + ``` + +- **`ERR_WEBEID_MISSING_PARAMETER`** + - **Thrown when:** Required parameter was missing. + - **Likely reason:** While calling a library function, a required parameter was not provided. + - **How to resolve:** The error message might contain helpful hints on what was missing. Check the documentation. + + +## Development + +**You are welcome to create pull requests for the Web-eID library!** + +- Make sure your code editor takes advantage of the `.editorconfig` file. + It lets your code editor know what the basic formatting rules for this project should be. + See: [https://editorconfig.org](https://editorconfig.org) +- Run `npm install` to install dependencies. +- Run `npm run lint` before making a pull request. +- Run `npm run build` to build compile the project and generate bundles. + The build process will run the following commands in sequence. + + | Command | Description | + |-|-| + | `npm run clean` | Removes the `./dist` directory. | + | `npm run compile` | Runs the TypeScript compiler, generates:
`./dist/node` | + | `npm run bundle` | Runs the Rollup bundler, generates:
`./dist/es`
`./dist/iife`
`./dist/umd`| + + +### Testing changes locally + +When you've made changes to the library and wish to test the behavior within another project, there are a couple of ways to do it. + + +#### Using `npm link` + +You can use `npm link` to link the Web-eID library source directory to a another project. + +**Example** +```bash +cd ~/workspace/web-eid-library +npm run build + +cd ~/workspace/example-website +npm link ~/workspace/web-eid-library +``` + +**Pros** +- This option might be more convenient for active development. +- After making changes to the Web-eID library source and rebuilding the library project, you don't need to reinstall the dependency in your other project. + +**Cons** +- ES modules are located at `web-eid/dist/node/` instead of directly under `web-eid/`. + ```ts + import AuthenticateOptions from 'web-eid/dist/node/models/AuthenticateOptions'; + ``` + +#### Using `npm pack` + +You can use `npm pack` to generate a package and install the package archive. + +**Example** +```bash +cd ~/workspace/web-eid-library +npm run build +npm package + +cd ~/workspace/example-website +npm install ~/workspace/web-eid-library/web-eid-*.tgz +``` + +**Pros** +- Accurately simulates a installing from the NPM repository. +- Shorter import path. + ```ts + import AuthenticateOptions from 'web-eid/models/AuthenticateOptions'; + ``` + +**Cons** +- After making changes to the library, you need to rebuild the package and reinstall the library as a dependency. diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..248f0a2 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1246 @@ +{ + "name": "@web-eid/web-eid-library", + "version": "0.9.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@types/eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", + "dev": true + }, + "@types/json-schema": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz", + "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "2.19.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.19.2.tgz", + "integrity": "sha512-HX2qOq2GOV04HNrmKnTpSIpHjfl7iwdXe3u/Nvt+/cpmdvzYvY0NHSiTkYN257jHnq4OM/yo+OsFgati+7LqJA==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "2.19.2", + "eslint-utils": "^1.4.3", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^3.0.0", + "tsutils": "^3.17.1" + } + }, + "@typescript-eslint/experimental-utils": { + "version": "2.19.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.19.2.tgz", + "integrity": "sha512-B88QuwT1wMJR750YvTJBNjMZwmiPpbmKYLm1yI7PCc3x0NariqPwqaPsoJRwU9DmUi0cd9dkhz1IqEnwfD+P1A==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/typescript-estree": "2.19.2", + "eslint-scope": "^5.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "2.19.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.19.2.tgz", + "integrity": "sha512-8uwnYGKqX9wWHGPGdLB9sk9+12sjcdqEEYKGgbS8A0IvYX59h01o8os5qXUHMq2na8vpDRaV0suTLM7S8wraTA==", + "dev": true, + "requires": { + "@types/eslint-visitor-keys": "^1.0.0", + "@typescript-eslint/experimental-utils": "2.19.2", + "@typescript-eslint/typescript-estree": "2.19.2", + "eslint-visitor-keys": "^1.1.0" + } + }, + "@typescript-eslint/typescript-estree": { + "version": "2.19.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.19.2.tgz", + "integrity": "sha512-Xu/qa0MDk6upQWqE4Qy2X16Xg8Vi32tQS2PR0AvnT/ZYS4YGDvtn2MStOh5y8Zy2mg4NuL06KUHlvCh95j9C6Q==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "eslint-visitor-keys": "^1.1.0", + "glob": "^7.1.6", + "is-glob": "^4.0.1", + "lodash": "^4.17.15", + "semver": "^6.3.0", + "tsutils": "^3.17.1" + } + }, + "acorn": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz", + "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==", + "dev": true + }, + "acorn-jsx": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.1.0.tgz", + "integrity": "sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw==", + "dev": true + }, + "ajv": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", + "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-escapes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz", + "integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", + "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^1.4.3", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.1.2", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^7.0.0", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.14", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.3", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^6.1.2", + "strip-ansi": "^5.2.0", + "strip-json-comments": "^3.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + } + } + }, + "eslint-scope": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", + "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", + "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "dev": true + }, + "espree": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.1.2.tgz", + "integrity": "sha512-2iUPuuPP+yW1PZaMSDM9eyVf8D5P0Hi8h83YtZ5bPc/zHYjII5khoixIUTMO794NOY8F/ThF1Bo8ncZILarUTA==", + "dev": true, + "requires": { + "acorn": "^7.1.0", + "acorn-jsx": "^5.1.0", + "eslint-visitor-keys": "^1.1.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.1.0.tgz", + "integrity": "sha512-MxYW9xKmROWF672KqjO75sszsA8Mxhw06YFeS5VHlB98KDHbOSurm3ArsjO60Eaf3QmGMCP1yn+0JQkNLo/97Q==", + "dev": true, + "requires": { + "estraverse": "^4.0.0" + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "figures": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.1.0.tgz", + "integrity": "sha512-ravh8VRXqHuMvZt/d8GblBeqDMkdJMBdv/2KntFH+ra5MXkO7nxNKpzQ3n6QD/2da1kH0aWmNISdvhM7gl2gVg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + }, + "dependencies": { + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "flatted": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", + "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "optional": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", + "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.3.0.tgz", + "integrity": "sha512-wAfjdLgFsPZsklLJvOBUBmzYE8/CwhEqSBEMRXA3qxIiNtyqvjYurAtIfDh6chlEPUfmTY3MnZh5Hfh4q0UlIw==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-fresh": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "inquirer": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.0.4.tgz", + "integrity": "sha512-Bu5Td5+j11sCkqfqmUTiwv+tWisMtP0L7Q8WrqA2C/BbBhy1YTdFrvjjlrKq8oagA/tLQBski2Gcx/Sqyi2qSQ==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^2.4.2", + "cli-cursor": "^3.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.15", + "mute-stream": "0.0.8", + "run-async": "^2.2.0", + "rxjs": "^6.5.3", + "string-width": "^4.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "dev": true + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "regexpp": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.0.0.tgz", + "integrity": "sha512-Z+hNr7RAVWxznLPuA7DIh8UNX1j9CDrUQxskw9IrBE1Dxue2lyXT+shqEIeLUjrokxIP8CMy1WkjgG3rTsd5/g==", + "dev": true + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "rollup": { + "version": "2.26.11", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.26.11.tgz", + "integrity": "sha512-xyfxxhsE6hW57xhfL1I+ixH8l2bdoIMaAecdQiWF3N7IgJEMu99JG+daBiSZQjnBpzFxa0/xZm+3pbCdAQehHw==", + "dev": true, + "requires": { + "fsevents": "~2.1.2" + } + }, + "rollup-plugin-terser": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-5.3.1.tgz", + "integrity": "sha512-1pkwkervMJQGFYvM9nscrUoncPwiKR/K+bHdjv6PFgRo3cgPHoRT83y2Aa3GvINj4539S15t/tpFPb775TDs6w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.5.5", + "jest-worker": "^24.9.0", + "rollup-pluginutils": "^2.8.2", + "serialize-javascript": "^4.0.0", + "terser": "^4.6.2" + } + }, + "rollup-pluginutils": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", + "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", + "dev": true, + "requires": { + "estree-walker": "^0.6.1" + } + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "^2.1.0" + } + }, + "rxjs": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz", + "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + } + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + } + } + }, + "strip-json-comments": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", + "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } + } + }, + "terser": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", + "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", + "dev": true + }, + "tsutils": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", + "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, + "typescript": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", + "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "v8-compile-cache": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", + "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..76ff2f0 --- /dev/null +++ b/package.json @@ -0,0 +1,50 @@ +{ + "name": "@web-eid/web-eid-library", + "version": "0.9.0", + "description": "", + "scripts": { + "lint": "eslint . --ext .ts", + "build": "npm run clean && npm run compile && npm run bundle", + "clean": "rimraf ./dist", + "compile": "tsc", + "bundle": "rollup -c", + "test": "echo \"Error: no test specified\" && exit 1", + "prepare": "npm run build", + "prepack": "cp -R ./dist/node/* ./", + "postpack": "rimraf ./web-eid.js ./config.js ./errors ./models ./services ./utils ./*.d.ts ./*.map" + }, + "repository": { + "type": "git", + "url": "git@gitlab.com:web-eid/webextension/web-eid-library.git" + }, + "files": [ + "config.d.ts", + "config.d.ts.map", + "config.js", + "config.js.map", + "web-eid.d.ts", + "web-eid.d.ts.map", + "web-eid.js", + "web-eid.js.map", + "errors/**/*", + "models/**/*", + "services/**/*", + "utils/**/*", + "dist/es/*", + "dist/iife/*", + "dist/umd/*" + ], + "types": "web-eid.d.ts", + "author": "Tanel Metsar", + "license": "MIT", + "devDependencies": { + "@typescript-eslint/eslint-plugin": "^2.19.2", + "@typescript-eslint/parser": "^2.19.2", + "eslint": "^6.8.0", + "rimraf": "^3.0.2", + "rollup": "^2.26.11", + "rollup-plugin-terser": "^5.3.1", + "typescript": "^3.8.3" + }, + "dependencies": {} +} diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000..a82916a --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,38 @@ +import { terser } from "rollup-plugin-terser"; + +export default { + input: "./dist/node/web-eid.js", + + output: [ + { + file: "dist/iife/web-eid.js", + format: "iife", + name: "webeid", + sourcemap: true, + }, + { + file: "dist/iife/web-eid.min.js", + format: "iife", + name: "webeid", + sourcemap: false, + plugins: [terser()], + }, + { + file: "dist/umd/web-eid.js", + format: "umd", + name: "webeid", + sourcemap: true, + }, + { + file: "dist/umd/web-eid.min.js", + format: "umd", + name: "webeid", + sourcemap: false, + plugins: [terser()], + }, + { + file: "dist/es/web-eid.js", + format: "es", + }, + ], +}; diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 0000000..e6b5ea5 --- /dev/null +++ b/src/config.ts @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +export default Object.freeze({ + VERSION: "0.9.0", + EXTENSION_HANDSHAKE_TIMEOUT: 1000, // 1 second + NATIVE_APP_HANDSHAKE_TIMEOUT: 5 * 1000, // 5 seconds + DEFAULT_USER_INTERACTION_TIMEOUT: 2 * 60 * 1000, // 2 minutes + DEFAULT_SERVER_REQUEST_TIMEOUT: 20 * 1000, // 20 seconds +}); diff --git a/src/errors/ActionPendingError.ts b/src/errors/ActionPendingError.ts new file mode 100644 index 0000000..b266570 --- /dev/null +++ b/src/errors/ActionPendingError.ts @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import ErrorCode from "./ErrorCode"; + +export default class ActionPendingError extends Error { + public code: ErrorCode; + + constructor(message = "same action for Web-eID browser extension is already pending") { + super(message); + + this.name = this.constructor.name; + this.code = ErrorCode.ERR_WEBEID_ACTION_PENDING; + } +} diff --git a/src/errors/ActionTimeoutError.ts b/src/errors/ActionTimeoutError.ts new file mode 100644 index 0000000..301f6e7 --- /dev/null +++ b/src/errors/ActionTimeoutError.ts @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import ErrorCode from "./ErrorCode"; + +export default class ActionTimeoutError extends Error { + public code: ErrorCode; + + constructor(message = "extension message timeout") { + super(message); + + this.name = this.constructor.name; + this.code = ErrorCode.ERR_WEBEID_ACTION_TIMEOUT; + } +} diff --git a/src/errors/CertificateChangedError.ts b/src/errors/CertificateChangedError.ts new file mode 100644 index 0000000..2ccf493 --- /dev/null +++ b/src/errors/CertificateChangedError.ts @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import ErrorCode from "./ErrorCode"; + +export default class CertificateChangedError extends Error { + public code: ErrorCode; + + constructor(message = "server certificate changed between requests") { + super(message); + + this.name = this.constructor.name; + this.code = ErrorCode.ERR_WEBEID_CERTIFICATE_CHANGED; + } +} diff --git a/src/errors/ContextInsecureError.ts b/src/errors/ContextInsecureError.ts new file mode 100644 index 0000000..26789ac --- /dev/null +++ b/src/errors/ContextInsecureError.ts @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import ErrorCode from "./ErrorCode"; + +const SECURE_CONTEXTS_INFO_URL = "https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts"; + +export default class ContextInsecureError extends Error { + public code: ErrorCode; + + constructor(message = "Secure context required, see " + SECURE_CONTEXTS_INFO_URL) { + super(message); + + this.name = this.constructor.name; + this.code = ErrorCode.ERR_WEBEID_CONTEXT_INSECURE; + } +} diff --git a/src/errors/ErrorCode.ts b/src/errors/ErrorCode.ts new file mode 100644 index 0000000..a8545d4 --- /dev/null +++ b/src/errors/ErrorCode.ts @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +enum ErrorCode { + // Timeout errors + ERR_WEBEID_ACTION_TIMEOUT = "ERR_WEBEID_ACTION_TIMEOUT", + ERR_WEBEID_USER_TIMEOUT = "ERR_WEBEID_USER_TIMEOUT", + ERR_WEBEID_SERVER_TIMEOUT = "ERR_WEBEID_SERVER_TIMEOUT", + + // Health errors + ERR_WEBEID_VERSION_MISMATCH = "ERR_WEBEID_VERSION_MISMATCH", + ERR_WEBEID_VERSION_INVALID = "ERR_WEBEID_VERSION_INVALID", + ERR_WEBEID_EXTENSION_UNAVAILABLE = "ERR_WEBEID_EXTENSION_UNAVAILABLE", + ERR_WEBEID_NATIVE_UNAVAILABLE = "ERR_WEBEID_NATIVE_UNAVAILABLE", + ERR_WEBEID_UNKNOWN_ERROR = "ERR_WEBEID_UNKNOWN_ERROR", + + // Security errors + ERR_WEBEID_CONTEXT_INSECURE = "ERR_WEBEID_CONTEXT_INSECURE", + ERR_WEBEID_PROTOCOL_INSECURE = "ERR_WEBEID_PROTOCOL_INSECURE", + ERR_WEBEID_TLS_CONNECTION_BROKEN = "ERR_WEBEID_TLS_CONNECTION_BROKEN", + ERR_WEBEID_TLS_CONNECTION_INSECURE = "ERR_WEBEID_TLS_CONNECTION_INSECURE", + ERR_WEBEID_TLS_CONNECTION_WEAK = "ERR_WEBEID_TLS_CONNECTION_WEAK", + ERR_WEBEID_CERTIFICATE_CHANGED = "ERR_WEBEID_CERTIFICATE_CHANGED", + ERR_WEBEID_ORIGIN_MISMATCH = "ERR_WEBEID_ORIGIN_MISMATCH", + + // Third party errors + ERR_WEBEID_SERVER_REJECTED = "ERR_WEBEID_SERVER_REJECTED", + ERR_WEBEID_USER_CANCELLED = "ERR_WEBEID_USER_CANCELLED", + ERR_WEBEID_NATIVE_FATAL = "ERR_WEBEID_NATIVE_FATAL", + + // Developer mistakes + ERR_WEBEID_ACTION_PENDING = "ERR_WEBEID_ACTION_PENDING", + ERR_WEBEID_MISSING_PARAMETER = "ERR_WEBEID_MISSING_PARAMETER", +} + +export default ErrorCode; diff --git a/src/errors/ExtensionUnavailableError.ts b/src/errors/ExtensionUnavailableError.ts new file mode 100644 index 0000000..5115a27 --- /dev/null +++ b/src/errors/ExtensionUnavailableError.ts @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import ErrorCode from "./ErrorCode"; + +export default class ExtensionUnavailableError extends Error { + public code: ErrorCode; + + constructor(message = "Web-eID extension is not available") { + super(message); + + this.name = this.constructor.name; + this.code = ErrorCode.ERR_WEBEID_EXTENSION_UNAVAILABLE; + } +} diff --git a/src/errors/MissingParameterError.ts b/src/errors/MissingParameterError.ts new file mode 100644 index 0000000..5ff3461 --- /dev/null +++ b/src/errors/MissingParameterError.ts @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import ErrorCode from "./ErrorCode"; + +export default class MissingParameterError extends Error { + public code: ErrorCode; + + constructor(message: string) { + super(message); + + this.name = this.constructor.name; + this.code = ErrorCode.ERR_WEBEID_MISSING_PARAMETER; + } +} diff --git a/src/errors/NativeFatalError.ts b/src/errors/NativeFatalError.ts new file mode 100644 index 0000000..f56aa30 --- /dev/null +++ b/src/errors/NativeFatalError.ts @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import ErrorCode from "./ErrorCode"; + +export default class NativeFatalError extends Error { + public code: ErrorCode; + + constructor(message = "native application terminated with a fatal error") { + super(message); + + this.name = this.constructor.name; + this.code = ErrorCode.ERR_WEBEID_NATIVE_FATAL; + } +} diff --git a/src/errors/NativeUnavailableError.ts b/src/errors/NativeUnavailableError.ts new file mode 100644 index 0000000..8bf6ff3 --- /dev/null +++ b/src/errors/NativeUnavailableError.ts @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import ErrorCode from "./ErrorCode"; + +export default class NativeUnavailableError extends Error { + public code: ErrorCode; + + constructor(message = "Web-eID native application is not available") { + super(message); + + this.name = this.constructor.name; + this.code = ErrorCode.ERR_WEBEID_NATIVE_UNAVAILABLE; + } +} diff --git a/src/errors/OriginMismatchError.ts b/src/errors/OriginMismatchError.ts new file mode 100644 index 0000000..1501dda --- /dev/null +++ b/src/errors/OriginMismatchError.ts @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import ErrorCode from "./ErrorCode"; + +export default class OriginMismatchError extends Error { + public code: ErrorCode; + + constructor(message = "URLs for a single operation require the same origin") { + super(message); + + this.name = this.constructor.name; + this.code = ErrorCode.ERR_WEBEID_ORIGIN_MISMATCH; + } +} diff --git a/src/errors/ProtocolInsecureError.ts b/src/errors/ProtocolInsecureError.ts new file mode 100644 index 0000000..666a703 --- /dev/null +++ b/src/errors/ProtocolInsecureError.ts @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import ErrorCode from "./ErrorCode"; + +export default class ProtocolInsecureError extends Error { + public code: ErrorCode; + + constructor(message = "HTTPS required") { + super(message); + + this.name = this.constructor.name; + this.code = ErrorCode.ERR_WEBEID_PROTOCOL_INSECURE; + } +} diff --git a/src/errors/ServerRejectedError.ts b/src/errors/ServerRejectedError.ts new file mode 100644 index 0000000..ccd3667 --- /dev/null +++ b/src/errors/ServerRejectedError.ts @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import ErrorCode from "./ErrorCode"; + +export default class ServerRejectedError extends Error { + public code: ErrorCode; + public response?: Response; + + constructor(message = "server rejected the request") { + super(message); + + this.name = this.constructor.name; + this.code = ErrorCode.ERR_WEBEID_SERVER_REJECTED; + } +} diff --git a/src/errors/ServerTimeoutError.ts b/src/errors/ServerTimeoutError.ts new file mode 100644 index 0000000..f310d64 --- /dev/null +++ b/src/errors/ServerTimeoutError.ts @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import ErrorCode from "./ErrorCode"; + +export default class ServerTimeoutError extends Error { + public code: ErrorCode; + + constructor(message = "server failed to respond in time") { + super(message); + + this.name = this.constructor.name; + this.code = ErrorCode.ERR_WEBEID_SERVER_TIMEOUT; + } +} diff --git a/src/errors/TlsConnectionBrokenError.ts b/src/errors/TlsConnectionBrokenError.ts new file mode 100644 index 0000000..ad98c3a --- /dev/null +++ b/src/errors/TlsConnectionBrokenError.ts @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import ErrorCode from "./ErrorCode"; + +export default class TlsConnectionBrokenError extends Error { + public code: ErrorCode; + + constructor(message = "TLS connection was broken") { + super(message); + + this.name = this.constructor.name; + this.code = ErrorCode.ERR_WEBEID_TLS_CONNECTION_BROKEN; + } +} diff --git a/src/errors/TlsConnectionInsecureError.ts b/src/errors/TlsConnectionInsecureError.ts new file mode 100644 index 0000000..9e2d739 --- /dev/null +++ b/src/errors/TlsConnectionInsecureError.ts @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import ErrorCode from "./ErrorCode"; + +export default class TlsConnectionInsecureError extends Error { + public code: ErrorCode; + + constructor(message = "TLS connection was insecure") { + super(message); + + this.name = this.constructor.name; + this.code = ErrorCode.ERR_WEBEID_TLS_CONNECTION_INSECURE; + } +} diff --git a/src/errors/TlsConnectionWeakError.ts b/src/errors/TlsConnectionWeakError.ts new file mode 100644 index 0000000..d06d2f9 --- /dev/null +++ b/src/errors/TlsConnectionWeakError.ts @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import ErrorCode from "./ErrorCode"; + +export default class TlsConnectionWeakError extends Error { + public code: ErrorCode; + + constructor(message = "TLS connection was weak") { + super(message); + + this.name = this.constructor.name; + this.code = ErrorCode.ERR_WEBEID_TLS_CONNECTION_WEAK; + } +} diff --git a/src/errors/UnknownError.ts b/src/errors/UnknownError.ts new file mode 100644 index 0000000..7144468 --- /dev/null +++ b/src/errors/UnknownError.ts @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import ErrorCode from "./ErrorCode"; + +export default class UnknownError extends Error { + public code: ErrorCode; + + constructor(message = "an unknown error occurred") { + super(message); + + this.name = this.constructor.name; + this.code = ErrorCode.ERR_WEBEID_UNKNOWN_ERROR; + } +} diff --git a/src/errors/UserCancelledError.ts b/src/errors/UserCancelledError.ts new file mode 100644 index 0000000..b8d86c8 --- /dev/null +++ b/src/errors/UserCancelledError.ts @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import ErrorCode from "./ErrorCode"; + +export default class UserCancelledError extends Error { + public code: ErrorCode; + + constructor(message = "request was cancelled by the user") { + super(message); + + this.name = this.constructor.name; + this.code = ErrorCode.ERR_WEBEID_USER_CANCELLED; + } +} diff --git a/src/errors/UserTimeoutError.ts b/src/errors/UserTimeoutError.ts new file mode 100644 index 0000000..1c637f0 --- /dev/null +++ b/src/errors/UserTimeoutError.ts @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import ErrorCode from "./ErrorCode"; + +export default class UserTimeoutError extends Error { + public code: ErrorCode; + + constructor(message = "user failed to respond in time") { + super(message); + + this.name = this.constructor.name; + this.code = ErrorCode.ERR_WEBEID_USER_TIMEOUT; + } +} diff --git a/src/errors/VersionInvalidError.ts b/src/errors/VersionInvalidError.ts new file mode 100644 index 0000000..98c916c --- /dev/null +++ b/src/errors/VersionInvalidError.ts @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import ErrorCode from "./ErrorCode"; + +export default class VersionInvalidError extends Error { + public code: ErrorCode; + + constructor(message = "invalid version string") { + super(message); + + this.name = this.constructor.name; + this.code = ErrorCode.ERR_WEBEID_VERSION_INVALID; + } +} diff --git a/src/errors/VersionMismatchError.ts b/src/errors/VersionMismatchError.ts new file mode 100644 index 0000000..d4d4c58 --- /dev/null +++ b/src/errors/VersionMismatchError.ts @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import ErrorCode from "./ErrorCode"; +import Versions from "../models/Versions"; +import RequiresUpdate from "../models/RequiresUpdate"; + +function tmpl(strings: TemplateStringsArray, requiresUpdate: string): string { + return `Update required for Web-eID ${requiresUpdate}`; +} + +export default class VersionMismatchError extends Error { + public requiresUpdate: RequiresUpdate; + public code: ErrorCode; + public versions?: Versions; + public nativeApp?: string; + public extension?: string; + public library?: string; + + constructor(message: string | undefined, versions: Versions, requiresUpdate: RequiresUpdate) { + if (!message) { + if (!requiresUpdate) { + message = "requiresUpdate not provided"; + } else if (requiresUpdate.extension && requiresUpdate.nativeApp) { + message = tmpl`${"extension and native app"}`; + } else if (requiresUpdate.extension) { + message = tmpl`${"extension"}`; + } else if (requiresUpdate.nativeApp) { + message = tmpl`${"native app"}`; + } + } + + super(message); + + this.name = this.constructor.name; + this.code = ErrorCode.ERR_WEBEID_VERSION_MISMATCH; + this.requiresUpdate = requiresUpdate; + + if (versions) { + const { library, extension, nativeApp } = versions; + + Object.assign(this, { library, extension, nativeApp }); + } + } +} diff --git a/src/models/Action.ts b/src/models/Action.ts new file mode 100644 index 0000000..0d6f0cd --- /dev/null +++ b/src/models/Action.ts @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +enum Action { + STATUS = "web-eid:status", + STATUS_ACK = "web-eid:status-ack", + STATUS_SUCCESS = "web-eid:status-success", + STATUS_FAILURE = "web-eid:status-failure", + + AUTHENTICATE = "web-eid:authenticate", + AUTHENTICATE_ACK = "web-eid:authenticate-ack", + AUTHENTICATE_SUCCESS = "web-eid:authenticate-success", + AUTHENTICATE_FAILURE = "web-eid:authenticate-failure", + + SIGN = "web-eid:sign", + SIGN_ACK = "web-eid:sign-ack", + SIGN_SUCCESS = "web-eid:sign-success", + SIGN_FAILURE = "web-eid:sign-failure", +} + +export default Action; diff --git a/src/models/AuthenticateOptions.ts b/src/models/AuthenticateOptions.ts new file mode 100644 index 0000000..3c161b3 --- /dev/null +++ b/src/models/AuthenticateOptions.ts @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +export default interface AuthenticateOptions { + /** + * Authentication challenge GET request URL + * + * This URL should respond to a GET request with a cryptographic nonce. + * The request will be made by the browser extension. The extension will + * use this nonce to generate an authentication token. + * + * @example + * // Example response from the server + * {"nonce": ""} + */ + getAuthChallengeUrl: string; + + /** + * Authentication token POST request URL + * + * This URL should accept a JSON payload via a POST request which contains + * the authentication token generated by the browser extension. + * + * After the server validates the token,the server should respond + * with a 2xx status code and an optional JSON payload. + * + * If the provided token was invalid or the user should be forbidden access + * for a different reason, the server should respond with an appropriate + * HTTP error status code and an optional JSON payload. + * + * The authenticate method will resolve or reject with + * the optional JSON payload if the server chooses to provide it. + * + * @example + * // Example request payload + * {"token": ""} + */ + postAuthTokenUrl: string; + + /** + * Headers to append to the requests. + */ + headers?: { + [key: string]: string; + }; + + /** + * Time in milliseconds before a user interaction, for example PIN entry, times out. + * + * When not specified, defaults to 2 minutes. + */ + userInteractionTimeout?: number; + + /** + * Time in milliseconds before a server request, for example auth challenge request, times out. + * + * When not specified, defaults to 20 seconds. + */ + serverRequestTimeout?: number; +} diff --git a/src/models/HttpResponse.ts b/src/models/HttpResponse.ts new file mode 100644 index 0000000..ca124e7 --- /dev/null +++ b/src/models/HttpResponse.ts @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +export default interface HttpResponse { + /** + * The HTTP request's response headers. + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/Response/headers + */ + headers: object; + + /** + * A boolean indicating whether the response + * was successful (status in the range 200–299) or not. + */ + ok: boolean; + + /** + * Indicates whether or not the response is the result of a redirect. + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/Response/redirected + */ + redirected: boolean; + + /** + * The status code of the response. (This will be 200 for a success). + */ + status: number; + + /** + * The status message corresponding to the status code. (e.g., OK for 200). + */ + statusText: string; + + /** + * The type of the response (e.g., basic, cors). + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/Response/type + */ + type: string; + + /** + * The URL of the response. + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/Response/url + */ + url: string; + + /** + * Response body. Can be an object deserialized from JSON or plain text. + */ + body: object | string; +} diff --git a/src/models/Message.ts b/src/models/Message.ts new file mode 100644 index 0000000..7e4158d --- /dev/null +++ b/src/models/Message.ts @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +export default interface Message { + action: string; + + [key: string]: any; +} diff --git a/src/models/PendingMessage.ts b/src/models/PendingMessage.ts new file mode 100644 index 0000000..215449e --- /dev/null +++ b/src/models/PendingMessage.ts @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import Message from "./Message"; + +export default interface PendingMessage { + message: Message; + promise?: Promise; + resolve?: Function; + reject?: Function; + ackTimer?: number; + replyTimer?: number; +} diff --git a/src/models/RequiresUpdate.ts b/src/models/RequiresUpdate.ts new file mode 100644 index 0000000..e06b7d7 --- /dev/null +++ b/src/models/RequiresUpdate.ts @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +export default interface RequiresUpdate { + nativeApp: boolean; + extension: boolean; +} diff --git a/src/models/ResponseAuthenticateFailure.ts b/src/models/ResponseAuthenticateFailure.ts new file mode 100644 index 0000000..17a791c --- /dev/null +++ b/src/models/ResponseAuthenticateFailure.ts @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import Message from "./Message"; +import HttpResponse from "./HttpResponse"; + +export default interface ResponseAuthenticateFailure extends Message { + code: string; + response?: HttpResponse; +} diff --git a/src/models/ResponseAuthenticateSuccess.ts b/src/models/ResponseAuthenticateSuccess.ts new file mode 100644 index 0000000..0964c0b --- /dev/null +++ b/src/models/ResponseAuthenticateSuccess.ts @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import Message from "./Message"; +import HttpResponse from "./HttpResponse"; + +export default interface ResponseAuthenticateSuccess extends Message { + response: HttpResponse; +} diff --git a/src/models/ResponseSignFailure.ts b/src/models/ResponseSignFailure.ts new file mode 100644 index 0000000..9587c37 --- /dev/null +++ b/src/models/ResponseSignFailure.ts @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import Message from "./Message"; +import HttpResponse from "./HttpResponse"; + +export default interface ResponseSignFailure extends Message { + code: string; + response?: HttpResponse; +} diff --git a/src/models/ResponseSignSuccess.ts b/src/models/ResponseSignSuccess.ts new file mode 100644 index 0000000..9fa6b95 --- /dev/null +++ b/src/models/ResponseSignSuccess.ts @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import Message from "./Message"; +import HttpResponse from "./HttpResponse"; + +export default interface ResponseSignSuccess extends Message { + response: HttpResponse; +} diff --git a/src/models/ResponseStatusFailure.ts b/src/models/ResponseStatusFailure.ts new file mode 100644 index 0000000..5deefbb --- /dev/null +++ b/src/models/ResponseStatusFailure.ts @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import Message from "./Message"; + +export default interface ResponseStatusFailure extends Message { + code: string; + message?: string; + extension: string; + nativeApp?: string; +} diff --git a/src/models/ResponseStatusSuccess.ts b/src/models/ResponseStatusSuccess.ts new file mode 100644 index 0000000..4f2ac0c --- /dev/null +++ b/src/models/ResponseStatusSuccess.ts @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import Message from "./Message"; + +export default interface ResponseStatusSuccess extends Message { + extension: string; + nativeApp: string; +} diff --git a/src/models/SignOptions.ts b/src/models/SignOptions.ts new file mode 100644 index 0000000..b7ca2dc --- /dev/null +++ b/src/models/SignOptions.ts @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +export default interface SignOptions { + /** + * Prepare document for signing POST request URL + * + * This request provides the signing certificate in the request body, + * the backend service should prepare the document for signing + * and return the document `hash` and `algorithm` of the hash, optionally arbitrary additional data. + */ + postPrepareSigningUrl: string; + + /** + * Signature POST request URL + * + * This request provides the document `hash`, `signature` and optionally additional data, the backend service should + * sign the document and optionally return any JSON payload to be returned by webeid.sign(...) + */ + postFinalizeSigningUrl: string; + + /** + * Headers to append to the requests. + */ + headers?: { + [key: string]: string; + }; + + /** + * Time in milliseconds before a user interaction, for example PIN entry, times out. + * + * When not specified, defaults to 2 minutes. + */ + userInteractionTimeout?: number; + + /** + * Time in milliseconds before a server request, for example prepare signing request, times out. + * + * When not specified, defaults to 20 seconds. + */ + serverRequestTimeout?: number; +} diff --git a/src/models/Versions.ts b/src/models/Versions.ts new file mode 100644 index 0000000..ba57953 --- /dev/null +++ b/src/models/Versions.ts @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +export default interface Versions { + library: string; + nativeApp?: string; + extension?: string; +} diff --git a/src/services/WebExtensionService.ts b/src/services/WebExtensionService.ts new file mode 100644 index 0000000..50a70c6 --- /dev/null +++ b/src/services/WebExtensionService.ts @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import { deserializeError } from "../utils/errorSerializer"; +import config from "../config"; +import Message from "../models/Message"; +import PendingMessage from "../models/PendingMessage"; +import ActionPendingError from "../errors/ActionPendingError"; +import ActionTimeoutError from "../errors/ActionTimeoutError"; +import ContextInsecureError from "../errors/ContextInsecureError"; +import ExtensionUnavailableError from "../errors/ExtensionUnavailableError"; + +export default class WebExtensionService { + private queue: PendingMessage[] = []; + + constructor() { + window.addEventListener("message", (event) => this.receive(event)); + } + + private receive(event: { data: Message }): void { + const message = event.data; + const suffix = message.action?.match(/success$|failure$|ack$/)?.[0]; + const initialAction = this.getInitialAction(message.action); + const pending = this.getPendingMessage(initialAction); + + if (suffix === "ack") { + console.log("ack message", message); + console.log("ack pending", pending?.message.action); + console.log("ack queue", JSON.stringify(this.queue)); + } + + if (pending) { + switch (suffix) { + case "ack": { + clearTimeout(pending.ackTimer); + + break; + } + + case "success": { + pending.resolve?.(message); + this.removeFromQueue(initialAction); + + break; + } + + case "failure": { + pending.reject?.(message.error ? deserializeError(message.error) : message); + this.removeFromQueue(initialAction); + + break; + } + } + } + } + + send(message: Message, timeout: number): Promise { + if (this.getPendingMessage(message.action)) { + return Promise.reject(new ActionPendingError()); + + } else if (!window.isSecureContext) { + return Promise.reject(new ContextInsecureError()); + + } else { + const pending: PendingMessage = { message }; + + this.queue.push(pending); + + pending.promise = new Promise((resolve, reject) => { + pending.resolve = resolve; + pending.reject = reject; + }); + + pending.ackTimer = setTimeout( + () => this.onAckTimeout(pending), + config.EXTENSION_HANDSHAKE_TIMEOUT, + ); + + pending.replyTimer = setTimeout( + () => this.onReplyTimeout(pending), + timeout, + ); + + window.postMessage(message, "*"); + + return pending.promise as Promise; + } + } + + onReplyTimeout(pending: PendingMessage): void { + console.log("onReplyTimeout", pending.message.action); + pending.reject?.(new ActionTimeoutError()); + + this.removeFromQueue(pending.message.action); + } + + onAckTimeout(pending: PendingMessage): void { + console.log("onAckTimeout", pending.message.action); + pending.reject?.(new ExtensionUnavailableError()); + + clearTimeout(pending.replyTimer); + } + + getPendingMessage(action: string): PendingMessage | undefined { + return this.queue.find((pm) => { + return pm.message.action === action; + }); + } + + getSuccessAction(action: string): string { + return `${action}-success`; + } + + getFailureAction(action: string): string { + return `${action}-failure`; + } + + getInitialAction(action: string): string { + return action.replace(/-success$|-failure$|-ack$/, ""); + } + + removeFromQueue(action: string): void { + const pending = this.getPendingMessage(action); + + clearTimeout(pending?.replyTimer); + + this.queue = this.queue.filter((pending) => ( + pending.message.action !== action + )); + } +} diff --git a/src/utils/defer.ts b/src/utils/defer.ts new file mode 100644 index 0000000..0c7d57d --- /dev/null +++ b/src/utils/defer.ts @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +export default function defer(): Promise { + return new Promise((resolve) => setTimeout(resolve)); +} diff --git a/src/utils/errorSerializer.ts b/src/utils/errorSerializer.ts new file mode 100644 index 0000000..06e5dd2 --- /dev/null +++ b/src/utils/errorSerializer.ts @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import CertificateChangedError from "../errors/CertificateChangedError"; +import OriginMismatchError from "../errors/OriginMismatchError"; +import ContextInsecureError from "../errors/ContextInsecureError"; +import ExtensionUnavailableError from "../errors/ExtensionUnavailableError"; +import ActionPendingError from "../errors/ActionPendingError"; +import NativeFatalError from "../errors/NativeFatalError"; +import NativeUnavailableError from "../errors/NativeUnavailableError"; +import ServerRejectedError from "../errors/ServerRejectedError"; +import ErrorCode from "../errors/ErrorCode"; +import UserTimeoutError from "../errors/UserTimeoutError"; +import UserCancelledError from "../errors/UserCancelledError"; +import VersionMismatchError from "../errors/VersionMismatchError"; +import TlsConnectionBrokenError from "../errors/TlsConnectionBrokenError"; +import TlsConnectionInsecureError from "../errors/TlsConnectionInsecureError"; +import TlsConnectionWeakError from "../errors/TlsConnectionWeakError"; +import ProtocolInsecureError from "../errors/ProtocolInsecureError"; +import ActionTimeoutError from "../errors/ActionTimeoutError"; +import VersionInvalidError from "../errors/VersionInvalidError"; +import ServerTimeoutError from "../errors/ServerTimeoutError"; +import UnknownError from "../errors/UnknownError"; + +const errorCodeToErrorClass: {[key: string]: any} = { + [ErrorCode.ERR_WEBEID_ACTION_PENDING]: ActionPendingError, + [ErrorCode.ERR_WEBEID_ACTION_TIMEOUT]: ActionTimeoutError, + [ErrorCode.ERR_WEBEID_CERTIFICATE_CHANGED]: CertificateChangedError, + [ErrorCode.ERR_WEBEID_ORIGIN_MISMATCH]: OriginMismatchError, + [ErrorCode.ERR_WEBEID_CONTEXT_INSECURE]: ContextInsecureError, + [ErrorCode.ERR_WEBEID_EXTENSION_UNAVAILABLE]: ExtensionUnavailableError, + [ErrorCode.ERR_WEBEID_NATIVE_FATAL]: NativeFatalError, + [ErrorCode.ERR_WEBEID_NATIVE_UNAVAILABLE]: NativeUnavailableError, + [ErrorCode.ERR_WEBEID_PROTOCOL_INSECURE]: ProtocolInsecureError, + [ErrorCode.ERR_WEBEID_SERVER_REJECTED]: ServerRejectedError, + [ErrorCode.ERR_WEBEID_SERVER_TIMEOUT]: ServerTimeoutError, + [ErrorCode.ERR_WEBEID_TLS_CONNECTION_BROKEN]: TlsConnectionBrokenError, + [ErrorCode.ERR_WEBEID_TLS_CONNECTION_INSECURE]: TlsConnectionInsecureError, + [ErrorCode.ERR_WEBEID_TLS_CONNECTION_WEAK]: TlsConnectionWeakError, + [ErrorCode.ERR_WEBEID_USER_CANCELLED]: UserCancelledError, + [ErrorCode.ERR_WEBEID_USER_TIMEOUT]: UserTimeoutError, + [ErrorCode.ERR_WEBEID_VERSION_INVALID]: VersionInvalidError, + [ErrorCode.ERR_WEBEID_VERSION_MISMATCH]: VersionMismatchError, +}; + +export function serializeError(error: any): any { + const { + message, + name, + fileName, + lineNumber, + columnNumber, + stack, + } = error; + + return { + ...( + Object.fromEntries( + Object.getOwnPropertyNames(error) + .map((prop) => [prop, error[prop]]) + ) + ), + + message, + name, + fileName, + lineNumber, + columnNumber, + stack, + }; +} + +export function deserializeError(errorObject: any): any { + let error; + + if (typeof errorObject.code == "string" && errorObject.code in errorCodeToErrorClass) { + const CustomError = errorCodeToErrorClass[errorObject.code]; + + error = new CustomError(); + } else { + error = new UnknownError(); + } + + for (const [key, value] of Object.entries(errorObject)) { + error[key] = value; + } + + return error; +} diff --git a/src/utils/semver.ts b/src/utils/semver.ts new file mode 100644 index 0000000..f215118 --- /dev/null +++ b/src/utils/semver.ts @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import VersionInvalidError from "../errors/VersionInvalidError"; + +const semverPattern = new RegExp( + "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)" + + "(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" +); + +export enum IdentifierDiff { + NEWER = 1, + SAME = 0, + OLDER = -1, +} + +interface Semver { + major: number; + minor: number; + patch: number; + rc?: string; + build?: string; + string: string; +} + +/** + * The difference can be -1, 0 or 1. + */ +interface SemverDiff { + major: IdentifierDiff; + minor: IdentifierDiff; + patch: IdentifierDiff; +} + +export function parseSemver(string = ""): Semver { + const result = string.match(semverPattern); + + const [, majorStr, minorStr, patchStr, rc, build] = result ? result : []; + + const major = parseInt(majorStr, 10); + const minor = parseInt(minorStr, 10); + const patch = parseInt(patchStr, 10); + + for (const indentifier of [major, minor, patch]) { + if (Number.isNaN(indentifier)) { + throw new VersionInvalidError(`Invalid SemVer string '${string}'`); + } + } + + return { major, minor, patch, rc, build, string }; +} + +/** + * Compares two Semver objects. + * + * @param {Semver} a First SemVer object + * @param {Semver} b Second Semver object + * + * @returns {SemverDiff} Diff for major, minor and patch. + */ +export function compareSemver(a: Semver, b: Semver): SemverDiff { + return { + major: Math.sign(a.major - b.major), + minor: Math.sign(a.minor - b.minor), + patch: Math.sign(a.patch - b.patch), + }; +} diff --git a/src/utils/version.ts b/src/utils/version.ts new file mode 100644 index 0000000..bc0e3c7 --- /dev/null +++ b/src/utils/version.ts @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import Versions from "../models/Versions"; +import { parseSemver, compareSemver, IdentifierDiff } from "./semver"; +import UpdateRequired from "../models/RequiresUpdate"; + +/** + * Checks if update is required. + * + * @param version Object containing SemVer version strings for library, extension and native app. + * + * @returns Object which specifies if the extension or native app should be updated. + */ +export function checkCompatibility(version: Versions): UpdateRequired { + const library = parseSemver(version.library); + const extension = parseSemver(version.extension); + const nativeApp = parseSemver(version.nativeApp); + + const extensionDiff = compareSemver(extension, library); + const nativeAppDiff = compareSemver(nativeApp, library); + + return { + extension: extensionDiff.major === IdentifierDiff.OLDER, + nativeApp: nativeAppDiff.major === IdentifierDiff.OLDER, + }; +} + +/** + * Checks an object if 'library', 'extension' or 'nativeApp' properties are present. + * Values are not checked for SemVer validity. + * + * @param object Object which will be checked for version properties. + * + * @returns Were any of the version properties found in the provided object. + */ +export function hasVersionProperties(object: any): boolean { + if (typeof object === "object") { + for (const prop of ["library", "extension", "nativeApp"]) { + if (Object.hasOwnProperty.call(object, prop)) return true; + } + } + + return false; +} diff --git a/src/web-eid.ts b/src/web-eid.ts new file mode 100644 index 0000000..cea3119 --- /dev/null +++ b/src/web-eid.ts @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2020 The Web eID Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import config from "./config"; + +import ErrorCode from "./errors/ErrorCode"; + +import Action from "./models/Action"; +import AuthenticateOptions from "./models/AuthenticateOptions"; +import SignOptions from "./models/SignOptions"; +import Versions from "./models/Versions"; +import ResponseAuthenticateSuccess from "./models/ResponseAuthenticateSuccess"; +import ResponseStatusSuccess from "./models/ResponseStatusSuccess"; + +import WebExtensionService from "./services/WebExtensionService"; + +import * as version from "./utils/version"; +import VersionMismatchError from "./errors/VersionMismatchError"; +import HttpResponse from "./models/HttpResponse"; +import MissingParameterError from "./errors/MissingParameterError"; +import defer from "./utils/defer"; +import ResponseSignSuccess from "./models/ResponseSignSuccess"; + + +const webExtensionService = new WebExtensionService(); + +export async function status(): Promise { + await defer(); // Give chrome a moment to load the extension content script + + let statusResponse; + + const library = config.VERSION; + + const timeout = config.EXTENSION_HANDSHAKE_TIMEOUT + config.NATIVE_APP_HANDSHAKE_TIMEOUT; + const message = { action: Action.STATUS }; + + try { + statusResponse = await webExtensionService.send(message, timeout); + } catch (error) { + error.library = library; + + throw error; + } + + const versions: Versions = { library, ...statusResponse }; + + const requiresUpdate = version.checkCompatibility(versions); + + if (requiresUpdate.extension || requiresUpdate.nativeApp) { + throw new VersionMismatchError(undefined, versions, requiresUpdate); + } + + return versions; +} + +export async function authenticate(options: AuthenticateOptions): Promise { + await defer(); // Give chrome a moment to load the extension content script + + if (typeof options != "object") { + throw new MissingParameterError("authenticate function requires an options object as parameter"); + } + + if (!options.getAuthChallengeUrl) { + throw new MissingParameterError("getAuthChallengeUrl missing from authenticate options"); + } + + if (!options.postAuthTokenUrl) { + throw new MissingParameterError("postAuthTokenUrl missing from authenticate options"); + } + + const timeout = ( + config.EXTENSION_HANDSHAKE_TIMEOUT + + config.NATIVE_APP_HANDSHAKE_TIMEOUT + + (options.serverRequestTimeout || config.DEFAULT_SERVER_REQUEST_TIMEOUT) * 2 + + (options.userInteractionTimeout || config.DEFAULT_USER_INTERACTION_TIMEOUT) + ); + + const message = { ...options, action: Action.AUTHENTICATE }; + + const result = await webExtensionService.send(message, timeout); + + return result.response; +} + +export async function sign(options: SignOptions): Promise { + await defer(); // Give chrome a moment to load the extension content script + + if (typeof options != "object") { + throw new MissingParameterError("sign function requires an options object as parameter"); + } + + if (!options.postPrepareSigningUrl) { + throw new MissingParameterError("postPrepareSigningUrl missing from sign options"); + } + + if (!options.postFinalizeSigningUrl) { + throw new MissingParameterError("postFinalizeSigningUrl missing from sign options"); + } + + const timeout = ( + config.EXTENSION_HANDSHAKE_TIMEOUT + + config.NATIVE_APP_HANDSHAKE_TIMEOUT + + (options.serverRequestTimeout || config.DEFAULT_SERVER_REQUEST_TIMEOUT) * 2 + + (options.userInteractionTimeout || config.DEFAULT_USER_INTERACTION_TIMEOUT) * 2 + ); + + const message = { ...options, action: Action.SIGN }; + + const result = await webExtensionService.send(message, timeout); + + return result.response; +} + +export { Action, ErrorCode }; +export { hasVersionProperties } from "./utils/version"; +export { config }; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..9810c96 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "module": "es6", + "target": "es2019", + "noImplicitAny": true, + "declaration": true, + "declarationMap": true, + "moduleResolution": "node", + "sourceMap": true, + "strict": true, + "outDir": "dist/node", + "baseUrl": "." + }, + "include": ["src"], + "exclude": ["node_modules", "**/__tests__/*"] +}