diff --git a/lib/main.js b/lib/main.js index 44b85fc2..d823cd95 100644 --- a/lib/main.js +++ b/lib/main.js @@ -1,4 +1,5 @@ import * as utils from "./utils.js"; +import { ready } from "./toolbox.js"; import { Fido2AssertionResult, Fido2AttestationResult, Fido2Result } from "./response.js"; @@ -166,6 +167,7 @@ class Fido2Lib { * @see MdsCollection */ static async addMdsCollection(mdsCollection) { + await ready; if (!(mdsCollection instanceof MdsCollection)) { throw new Error( "expected 'mdsCollection' to be instance of MdsCollection, got: " + @@ -386,6 +388,7 @@ class Fido2Lib { * @private */ static async validateAttestation() { + await ready; const fmt = this.authnrData.get("fmt"); // validate input @@ -542,6 +545,7 @@ class Fido2Lib { * @throws {Error} If parsing or validation fails */ async attestationResult(res, expected) { + await ready; expected.flags = factorToFlags(expected.factor, ["AT"]); delete expected.factor; return await Fido2AttestationResult.create(res, expected); @@ -574,6 +578,7 @@ class Fido2Lib { */ // deno-lint-ignore require-await async assertionResult(res, expected) { + await ready; expected.flags = factorToFlags(expected.factor, []); delete expected.factor; return Fido2AssertionResult.create(res, expected); @@ -594,6 +599,7 @@ class Fido2Lib { * @returns {Promise} The options for creating calling `navigator.credentials.create()` */ async attestationOptions(opts) { + await ready; opts = opts || {}; // The object being returned is described here: @@ -714,6 +720,7 @@ class Fido2Lib { * @returns {Promise} The options to be passed to `navigator.credentials.get()` */ async assertionOptions(opts) { + await ready; opts = opts || {}; // https://w3c.github.io/webauthn/#dictdef-publickeycredentialcreationoptions diff --git a/lib/toolbox.js b/lib/toolbox.js index d1411486..132e0665 100644 --- a/lib/toolbox.js +++ b/lib/toolbox.js @@ -18,25 +18,30 @@ import base64 from "@hexagon/base64"; import { Certificate } from "./certUtils.js"; import { PublicKey } from "./keyUtils.js"; + // Import webcrypto -import * as platformCrypto from "crypto"; -import * as peculiarCrypto from "@peculiar/webcrypto"; -let webcrypto; -if ((typeof self !== "undefined") && "crypto" in self) { +let webcrypto = undefined; +if ((typeof self !== "undefined") && ("crypto" in self)) { // Always use crypto if available natively (browser / Deno) webcrypto = self.crypto; +} +const usePolyfill = async () => { + const module = await import("@peculiar/webcrypto"); + webcrypto = new module.Crypto(); +}; +const useNative = async () => { + // Always use node webcrypto if available ( Node >= 16.0 ) + // This also allows bundlers to resolve crypto with a custom polyfill + const module = await import("crypto"); + webcrypto = module.webcrypto; +}; -} else { - // Always use node webcrypto if available ( >= 16.0 ) - if(platformCrypto && platformCrypto.webcrypto) { - webcrypto = platformCrypto.webcrypto; - - } else { - // Fallback to @peculiar/webcrypto - webcrypto = new peculiarCrypto.Crypto(); - } +let ready = Promise.resolve(); +if (webcrypto === undefined) { + ready = ready.then(useNative).catch(usePolyfill); } + // Set up pkijs const pkijs = { setEngine, @@ -46,15 +51,23 @@ const pkijs = { CertificateChainValidationEngine, PublicKeyInfo, }; -pkijs.setEngine( - "newEngine", - webcrypto, - new pkijs.CryptoEngine({ - name: "", - crypto: webcrypto, - subtle: webcrypto.subtle, - }), -); +const setUpPkijs = () => { + pkijs.setEngine( + "newEngine", + webcrypto, + new pkijs.CryptoEngine({ + name: "", + crypto: webcrypto, + subtle: webcrypto.subtle, + }), + ); +}; +if (webcrypto === undefined) { + ready = ready.then(setUpPkijs); +} +else { + setUpPkijs(); +} function extractBigNum(fullArray, start, end, expectedLength) { let num = fullArray.slice(start, end); @@ -363,5 +376,6 @@ export { pkijs, randomValues, verifySignature, - webcrypto + webcrypto, + ready }; diff --git a/package-lock.json b/package-lock.json index fbfd35e7..1085c719 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,15 @@ { "name": "fido2-lib", - "version": "3.4.1", + "version": "3.4.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "fido2-lib", - "version": "3.4.1", + "version": "3.4.3", "license": "MIT", "dependencies": { "@hexagon/base64": "~1.1.26", - "@peculiar/webcrypto": "~1.4.3", "asn1js": "~3.0.2", "cbor-x": "~1.5.3", "jose": "^4.14.4", @@ -31,6 +30,9 @@ }, "engines": { "node": ">=10" + }, + "optionalDependencies": { + "@peculiar/webcrypto": "~1.4.3" } }, "node_modules/@babel/parser": { @@ -320,6 +322,7 @@ "version": "2.3.6", "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.3.6.tgz", "integrity": "sha512-izNRxPoaeJeg/AyH8hER6s+H7p4itk+03QCa4sbxI3lNdseQYCuxzgsuNK8bTXChtLTjpJz6NmXKA73qLa3rCA==", + "optional": true, "dependencies": { "asn1js": "^3.0.5", "pvtsutils": "^1.3.2", @@ -330,6 +333,7 @@ "version": "1.1.12", "resolved": "https://registry.npmjs.org/@peculiar/json-schema/-/json-schema-1.1.12.tgz", "integrity": "sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==", + "optional": true, "dependencies": { "tslib": "^2.0.0" }, @@ -341,6 +345,7 @@ "version": "1.4.3", "resolved": "https://registry.npmjs.org/@peculiar/webcrypto/-/webcrypto-1.4.3.tgz", "integrity": "sha512-VtaY4spKTdN5LjJ04im/d/joXuvLbQdgy5Z4DXF4MFZhQ+MTrejbNMkfZBp1Bs3O5+bFqnJgyGdPuZQflvIa5A==", + "optional": true, "dependencies": { "@peculiar/asn1-schema": "^2.3.6", "@peculiar/json-schema": "^1.1.12", @@ -2946,6 +2951,7 @@ "version": "1.7.7", "resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.7.7.tgz", "integrity": "sha512-7FjigXNsBfopEj+5DV2nhNpfic2vumtjjgPmeDKk45z+MJwXKKfhPB7118Pfzrmh4jqOMST6Ch37iPAHoImg5g==", + "optional": true, "dependencies": { "@peculiar/asn1-schema": "^2.3.6", "@peculiar/json-schema": "^1.1.12", @@ -3292,6 +3298,7 @@ "version": "2.3.6", "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.3.6.tgz", "integrity": "sha512-izNRxPoaeJeg/AyH8hER6s+H7p4itk+03QCa4sbxI3lNdseQYCuxzgsuNK8bTXChtLTjpJz6NmXKA73qLa3rCA==", + "optional": true, "requires": { "asn1js": "^3.0.5", "pvtsutils": "^1.3.2", @@ -3302,6 +3309,7 @@ "version": "1.1.12", "resolved": "https://registry.npmjs.org/@peculiar/json-schema/-/json-schema-1.1.12.tgz", "integrity": "sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==", + "optional": true, "requires": { "tslib": "^2.0.0" } @@ -3310,6 +3318,7 @@ "version": "1.4.3", "resolved": "https://registry.npmjs.org/@peculiar/webcrypto/-/webcrypto-1.4.3.tgz", "integrity": "sha512-VtaY4spKTdN5LjJ04im/d/joXuvLbQdgy5Z4DXF4MFZhQ+MTrejbNMkfZBp1Bs3O5+bFqnJgyGdPuZQflvIa5A==", + "optional": true, "requires": { "@peculiar/asn1-schema": "^2.3.6", "@peculiar/json-schema": "^1.1.12", @@ -5254,6 +5263,7 @@ "version": "1.7.7", "resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.7.7.tgz", "integrity": "sha512-7FjigXNsBfopEj+5DV2nhNpfic2vumtjjgPmeDKk45z+MJwXKKfhPB7118Pfzrmh4jqOMST6Ch37iPAHoImg5g==", + "optional": true, "requires": { "@peculiar/asn1-schema": "^2.3.6", "@peculiar/json-schema": "^1.1.12", diff --git a/package.json b/package.json index 22feb223..26e2d688 100644 --- a/package.json +++ b/package.json @@ -49,13 +49,15 @@ }, "dependencies": { "@hexagon/base64": "~1.1.26", - "@peculiar/webcrypto": "~1.4.3", "asn1js": "~3.0.2", "cbor-x": "~1.5.3", "jose": "^4.14.4", "pkijs": "~3.0.15", "tldts": "~6.0.5" }, + "optionalDependencies": { + "@peculiar/webcrypto": "~1.4.3" + }, "eslintConfig": { "root": true, "env": { diff --git a/types/main.d.ts b/types/main.d.ts index 2f95ead8..16c93a7f 100644 --- a/types/main.d.ts +++ b/types/main.d.ts @@ -1,8 +1,9 @@ /// -import {JWTPayload} from "jose/dist/types/types"; - declare module "fido2-lib" { + // Type imports in ambient module should use import(), + // see https://stackoverflow.com/questions/39040108 + type JWTPayload = import("jose/dist/types/types").JWTPayload; class MdsEntry{ constructor(mdsEntry: Object, tocEntry: Object)