nid-webauthn-emulator 0.2.13 → 0.2.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +7 -1
- package/README.md +7 -1
- package/dist/spec/authenticator/authenticator-emulator.spec.js +66 -65
- package/dist/spec/authenticator/ctap-model.spec.js +58 -53
- package/dist/spec/integration/integration.spec.js +7 -4
- package/dist/spec/integration/passkeys-api-client.d.ts +0 -1
- package/dist/spec/integration/webauthn-io.d.ts +0 -1
- package/dist/spec/libs/browser-injection.spec.js +11 -7
- package/dist/spec/libs/cbor.spec.js +48 -35
- package/dist/spec/libs/cose-key.spec.js +46 -40
- package/dist/spec/repository/credential-repository.spec.js +13 -12
- package/dist/spec/test-utils/unit-test.spec.js +28 -27
- package/dist/spec/webauthn/cose-key.spec.js +18 -17
- package/dist/spec/webauthn/webauthn-emulator.spec.js +138 -137
- package/dist/spec/webauthn/webauthn-model-json.spec.js +24 -23
- package/dist/spec/webauthn/webauthn-model.spec.js +25 -17
- package/dist/spec/webauthn/webauthn-test-server.d.ts +0 -1
- package/dist/spec/webauthn/webauthn-test-server.js +2 -2
- package/dist/src/repository/credentials-repository.d.ts +0 -1
- package/dist/src/test-utils/unit-test.d.ts +7 -5
- package/dist/src/webauthn/webauthn-emulator.d.ts +4 -4
- package/dist/src/webauthn/webauthn-model-json.d.ts +2 -94
- package/dist/src/webauthn/webauthn-model-json.js +1 -1
- package/package.json +6 -8
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const
|
|
6
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
7
|
+
const node_test_1 = require("node:test");
|
|
4
8
|
const browser_injection_1 = require("../../src/test-utils/browser-injection");
|
|
5
|
-
(0,
|
|
6
|
-
(0,
|
|
9
|
+
(0, node_test_1.describe)("Browser Injection Test", () => {
|
|
10
|
+
(0, node_test_1.test)("Browser Injection Test", async () => {
|
|
7
11
|
const window = { navigator: { credentials: { create: undefined, get: undefined } } };
|
|
8
12
|
const PublicKeyCredential = {
|
|
9
13
|
isConditionalMediationAvailable: async () => false,
|
|
@@ -11,9 +15,9 @@ const browser_injection_1 = require("../../src/test-utils/browser-injection");
|
|
|
11
15
|
};
|
|
12
16
|
// biome-ignore lint/security/noGlobalEval: This is a test code.
|
|
13
17
|
eval(browser_injection_1.HookWebAuthnApis);
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
strict_1.default.ok(await PublicKeyCredential.isConditionalMediationAvailable());
|
|
19
|
+
strict_1.default.notEqual(window.navigator.credentials.create, undefined);
|
|
20
|
+
strict_1.default.notEqual(window.navigator.credentials.get, undefined);
|
|
21
|
+
strict_1.default.notEqual(PublicKeyCredential.signalUnknownCredential, undefined);
|
|
18
22
|
});
|
|
19
23
|
});
|
|
@@ -3,21 +3,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const
|
|
6
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
7
|
+
const node_test_1 = require("node:test");
|
|
7
8
|
const encode_utils_1 = __importDefault(require("../../src/libs/encode-utils"));
|
|
8
|
-
(0,
|
|
9
|
-
(0,
|
|
9
|
+
(0, node_test_1.describe)("CBOR encode/decode edge cases", () => {
|
|
10
|
+
(0, node_test_1.test)("round-trips primitive values and large integer encodings", () => {
|
|
10
11
|
const encoded = encode_utils_1.default.encodeCbor({
|
|
11
12
|
1: "one",
|
|
12
13
|
nil: null,
|
|
13
14
|
values: [false, true, undefined, -2, 24, 0x100, 0x1_0000, 0x1_0000_0000],
|
|
14
15
|
});
|
|
15
16
|
const decoded = encode_utils_1.default.decodeCbor(encoded);
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
strict_1.default.equal(decoded[1], "one");
|
|
18
|
+
strict_1.default.equal(decoded.nil, null);
|
|
19
|
+
strict_1.default.deepEqual(decoded.values, [false, true, undefined, -2, 24, 0x100, 0x1_0000, 0x1_0000_0000]);
|
|
19
20
|
});
|
|
20
|
-
(0,
|
|
21
|
+
(0, node_test_1.test)("handles ArrayBuffer values within objects and arrays", () => {
|
|
21
22
|
const ab = new Uint8Array([1, 2, 3]).buffer; // ArrayBuffer branch
|
|
22
23
|
const ua = new Uint8Array([4, 5]);
|
|
23
24
|
const obj = {
|
|
@@ -28,49 +29,61 @@ const encode_utils_1 = __importDefault(require("../../src/libs/encode-utils"));
|
|
|
28
29
|
const encoded = encode_utils_1.default.encodeCbor(obj);
|
|
29
30
|
const decoded = encode_utils_1.default.decodeCbor(encoded);
|
|
30
31
|
// ArrayBuffer is decoded back to Uint8Array
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
strict_1.default.ok(decoded.v1 instanceof Uint8Array);
|
|
33
|
+
strict_1.default.deepEqual(Array.from(decoded.v1), [1, 2, 3]);
|
|
33
34
|
// Existing Uint8Array round-trips
|
|
34
|
-
|
|
35
|
+
strict_1.default.deepEqual(Array.from(decoded.v2), [4, 5]);
|
|
35
36
|
// Mixed array elements preserved as Uint8Array
|
|
36
|
-
|
|
37
|
+
strict_1.default.equal(Array.isArray(decoded.arr), true);
|
|
37
38
|
const [a0, a1] = decoded.arr;
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
strict_1.default.deepEqual(Array.from(a0), [1, 2, 3]);
|
|
40
|
+
strict_1.default.deepEqual(Array.from(a1), [4, 5]);
|
|
40
41
|
});
|
|
41
|
-
(0,
|
|
42
|
+
(0, node_test_1.test)("handles Buffer values without conversion in encoder and to Uint8Array on decode", () => {
|
|
42
43
|
const buf = Buffer.from([9, 8]);
|
|
43
44
|
const obj = { b: buf };
|
|
44
45
|
const encoded = encode_utils_1.default.encodeCbor(obj);
|
|
45
46
|
const decoded = encode_utils_1.default.decodeCbor(encoded);
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
strict_1.default.ok(decoded.b instanceof Uint8Array);
|
|
48
|
+
strict_1.default.deepEqual(Array.from(decoded.b), [9, 8]);
|
|
48
49
|
});
|
|
49
|
-
(0,
|
|
50
|
+
(0, node_test_1.test)("keeps non-exact integer object keys as text string map keys", () => {
|
|
50
51
|
const decoded = encode_utils_1.default.decodeCbor(encode_utils_1.default.encodeCbor({
|
|
51
52
|
"1": false,
|
|
52
53
|
"01": true,
|
|
53
54
|
"1abc": true,
|
|
54
55
|
}));
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
strict_1.default.equal(decoded[1], false);
|
|
57
|
+
strict_1.default.equal(decoded["01"], true);
|
|
58
|
+
strict_1.default.equal(decoded["1abc"], true);
|
|
58
59
|
});
|
|
59
|
-
(0,
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
(0, node_test_1.test)("decodes explicit integer widths", () => {
|
|
61
|
+
strict_1.default.equal(encode_utils_1.default.decodeCbor(new Uint8Array([0x1a, 0x00, 0x01, 0x00, 0x00])), 0x1_0000);
|
|
62
|
+
strict_1.default.equal(encode_utils_1.default.decodeCbor(new Uint8Array([0x1b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00])), 0x1_0000_0000);
|
|
63
|
+
strict_1.default.equal(encode_utils_1.default.decodeCbor(new Uint8Array([0xf6])), null);
|
|
63
64
|
});
|
|
64
|
-
(0,
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
65
|
+
(0, node_test_1.test)("rejects unsupported values and malformed input", () => {
|
|
66
|
+
strict_1.default.throws(() => encode_utils_1.default.encodeCbor(Symbol("unsupported")), {
|
|
67
|
+
message: "Unsupported CBOR value type: symbol",
|
|
68
|
+
});
|
|
69
|
+
strict_1.default.throws(() => encode_utils_1.default.encodeCbor({ value: Number.NaN }), { message: "CBOR only supports safe integers" });
|
|
70
|
+
strict_1.default.throws(() => encode_utils_1.default.decodeCbor(new Uint8Array()), { message: "Insufficient data" });
|
|
71
|
+
strict_1.default.throws(() => encode_utils_1.default.decodeCbor(new Uint8Array([0x42, 0x01])), { message: "Insufficient data" });
|
|
72
|
+
strict_1.default.throws(() => encode_utils_1.default.decodeCbor(new Uint8Array([0x5f])), {
|
|
73
|
+
message: "Unsupported indefinite-length CBOR data",
|
|
74
|
+
});
|
|
75
|
+
strict_1.default.throws(() => encode_utils_1.default.decodeCbor(new Uint8Array([0x01, 0x02])), {
|
|
76
|
+
message: "Unexpected trailing CBOR data",
|
|
77
|
+
});
|
|
78
|
+
strict_1.default.throws(() => encode_utils_1.default.decodeCbor(new Uint8Array([0xc1, 0x01])), {
|
|
79
|
+
message: "Unsupported CBOR tagged value",
|
|
80
|
+
});
|
|
81
|
+
strict_1.default.throws(() => encode_utils_1.default.decodeCbor(new Uint8Array([0xf8])), {
|
|
82
|
+
message: "Unsupported CBOR simple value: 24",
|
|
83
|
+
});
|
|
84
|
+
strict_1.default.throws(() => encode_utils_1.default.decodeCbor(new Uint8Array([0xa1, 0x41, 0x01, 0x01])), {
|
|
85
|
+
message: "Unsupported CBOR map key type",
|
|
86
|
+
});
|
|
87
|
+
strict_1.default.throws(() => encode_utils_1.default.decodeCbor(new Uint8Array([0x1b, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])), { message: "CBOR integer exceeds JavaScript safe integer range" });
|
|
75
88
|
});
|
|
76
89
|
});
|
|
@@ -1,47 +1,53 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const
|
|
6
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
7
|
+
const node_test_1 = require("node:test");
|
|
4
8
|
const authenticator_emulator_1 = require("../../src/authenticator/authenticator-emulator");
|
|
5
9
|
const cose_key_1 = require("../../src/webauthn/cose-key");
|
|
6
10
|
const webauthn_model_1 = require("../../src/webauthn/webauthn-model");
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
for (const alg of [-7, -8, -257]) {
|
|
12
|
+
(0, node_test_1.describe)(`CoseKey Test: ${alg}`, () => {
|
|
13
|
+
const getKey = () => {
|
|
14
|
+
const authenticator = new authenticator_emulator_1.AuthenticatorEmulator();
|
|
15
|
+
const rpId = new webauthn_model_1.RpId("example.com");
|
|
16
|
+
const userHandle = new Uint8Array([0x01, 0x02, 0x03, 0x04]);
|
|
17
|
+
const credential = authenticator.authenticatorMakeCredential({
|
|
18
|
+
rp: { id: rpId.value, name: "example" },
|
|
19
|
+
user: { id: userHandle, name: "example", displayName: "example" },
|
|
20
|
+
pubKeyCredParams: [{ alg, type: "public-key" }],
|
|
21
|
+
clientDataHash: new Uint8Array(32),
|
|
22
|
+
});
|
|
23
|
+
return (0, webauthn_model_1.unpackAuthenticatorData)(credential.authData).attestedCredentialData?.credentialPublicKey;
|
|
24
|
+
};
|
|
25
|
+
(0, node_test_1.test)("Der format _ serialize and deserialize test", async () => {
|
|
26
|
+
const testCoseKey = getKey();
|
|
27
|
+
const serialized = testCoseKey.toDer();
|
|
28
|
+
const deserialized = cose_key_1.CoseKey.fromDer(testCoseKey.toDer());
|
|
29
|
+
const reSerialized = deserialized.toDer();
|
|
30
|
+
strict_1.default.deepEqual(testCoseKey.alg, alg);
|
|
31
|
+
strict_1.default.deepEqual(testCoseKey, deserialized);
|
|
32
|
+
strict_1.default.deepEqual(serialized, reSerialized);
|
|
33
|
+
});
|
|
34
|
+
(0, node_test_1.test)("Jwk format _ serialize and deserialize test", async () => {
|
|
35
|
+
const testCoseKey = getKey();
|
|
36
|
+
const serialized = testCoseKey.toJwk();
|
|
37
|
+
const deserialized = cose_key_1.CoseKey.fromJwk(testCoseKey.toJwk()).toJwk();
|
|
38
|
+
const reSerialized = cose_key_1.CoseKey.fromJwk(deserialized).toJwk();
|
|
39
|
+
strict_1.default.deepEqual(testCoseKey.alg, alg);
|
|
40
|
+
strict_1.default.deepEqual(serialized, deserialized);
|
|
41
|
+
strict_1.default.deepEqual(serialized, reSerialized);
|
|
42
|
+
});
|
|
43
|
+
(0, node_test_1.test)("KeyObject format _ serialize and deserialize test", async () => {
|
|
44
|
+
const testCoseKey = getKey();
|
|
45
|
+
const serialized = testCoseKey.toKeyObject();
|
|
46
|
+
const deserialized = cose_key_1.CoseKey.fromKeyObject(testCoseKey.toKeyObject()).toKeyObject();
|
|
47
|
+
const reSerialized = cose_key_1.CoseKey.fromKeyObject(deserialized).toKeyObject();
|
|
48
|
+
strict_1.default.deepEqual(testCoseKey.alg, alg);
|
|
49
|
+
strict_1.default.deepEqual(serialized, deserialized);
|
|
50
|
+
strict_1.default.deepEqual(serialized, reSerialized);
|
|
17
51
|
});
|
|
18
|
-
return (0, webauthn_model_1.unpackAuthenticatorData)(credential.authData).attestedCredentialData?.credentialPublicKey;
|
|
19
|
-
};
|
|
20
|
-
(0, globals_1.test)("Der format _ serialize and deserialize test", async () => {
|
|
21
|
-
const testCoseKey = getKey();
|
|
22
|
-
const serialized = testCoseKey.toDer();
|
|
23
|
-
const deserialized = cose_key_1.CoseKey.fromDer(testCoseKey.toDer());
|
|
24
|
-
const reSerialized = deserialized.toDer();
|
|
25
|
-
(0, globals_1.expect)(testCoseKey.alg).toEqual(alg);
|
|
26
|
-
(0, globals_1.expect)(testCoseKey).toEqual(deserialized);
|
|
27
|
-
(0, globals_1.expect)(serialized).toEqual(reSerialized);
|
|
28
|
-
});
|
|
29
|
-
(0, globals_1.test)("Jwk format _ serialize and deserialize test", async () => {
|
|
30
|
-
const testCoseKey = getKey();
|
|
31
|
-
const serialized = testCoseKey.toJwk();
|
|
32
|
-
const deserialized = cose_key_1.CoseKey.fromJwk(testCoseKey.toJwk()).toJwk();
|
|
33
|
-
const reSerialized = cose_key_1.CoseKey.fromJwk(deserialized).toJwk();
|
|
34
|
-
(0, globals_1.expect)(testCoseKey.alg).toEqual(alg);
|
|
35
|
-
(0, globals_1.expect)(serialized).toEqual(deserialized);
|
|
36
|
-
(0, globals_1.expect)(serialized).toEqual(reSerialized);
|
|
37
|
-
});
|
|
38
|
-
(0, globals_1.test)("KeyObject format _ serialize and deserialize test", async () => {
|
|
39
|
-
const testCoseKey = getKey();
|
|
40
|
-
const serialized = testCoseKey.toKeyObject();
|
|
41
|
-
const deserialized = cose_key_1.CoseKey.fromKeyObject(testCoseKey.toKeyObject()).toKeyObject();
|
|
42
|
-
const reSerialized = cose_key_1.CoseKey.fromKeyObject(deserialized).toKeyObject();
|
|
43
|
-
(0, globals_1.expect)(testCoseKey.alg).toEqual(alg);
|
|
44
|
-
(0, globals_1.expect)(serialized).toEqual(deserialized);
|
|
45
|
-
(0, globals_1.expect)(serialized).toEqual(reSerialized);
|
|
46
52
|
});
|
|
47
|
-
}
|
|
53
|
+
}
|
|
@@ -36,13 +36,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
39
40
|
const fs = __importStar(require("node:fs"));
|
|
40
41
|
const node_path_1 = __importDefault(require("node:path"));
|
|
41
|
-
const
|
|
42
|
+
const node_test_1 = require("node:test");
|
|
42
43
|
const credentials_file_repository_1 = require("../../src/repository/credentials-file-repository");
|
|
43
44
|
const credentials_memory_repository_1 = require("../../src/repository/credentials-memory-repository");
|
|
44
45
|
const credentials_repository_1 = require("../../src/repository/credentials-repository");
|
|
45
|
-
(0,
|
|
46
|
+
(0, node_test_1.describe)("Credential Repository Test", () => {
|
|
46
47
|
const testCredentialJSON = {
|
|
47
48
|
publicKeyCredentialDescriptor: {
|
|
48
49
|
id: "9gd8oypZ_ccpqhk3RBjUp85EOK4NA1wtEkU7zr_BOd8",
|
|
@@ -63,24 +64,24 @@ const credentials_repository_1 = require("../../src/repository/credentials-repos
|
|
|
63
64
|
displayName: "example",
|
|
64
65
|
},
|
|
65
66
|
};
|
|
66
|
-
(0,
|
|
67
|
+
(0, node_test_1.test)("Serialize and Deserialize Test", async () => {
|
|
67
68
|
const deserialized = (0, credentials_repository_1.deserializeCredential)(JSON.stringify(testCredentialJSON));
|
|
68
69
|
const serialization = (0, credentials_repository_1.serializeCredential)(deserialized);
|
|
69
70
|
const reDeserialized = (0, credentials_repository_1.deserializeCredential)(serialization);
|
|
70
|
-
(
|
|
71
|
+
strict_1.default.deepEqual(reDeserialized, deserialized);
|
|
71
72
|
});
|
|
72
|
-
(0,
|
|
73
|
+
(0, node_test_1.test)("Memory Repository Test", async () => {
|
|
73
74
|
const deserialized = (0, credentials_repository_1.deserializeCredential)(JSON.stringify(testCredentialJSON));
|
|
74
75
|
const repository = new credentials_memory_repository_1.PasskeysCredentialsMemoryRepository();
|
|
75
76
|
repository.saveCredential(deserialized);
|
|
76
77
|
const loaded = repository.loadCredentials();
|
|
77
|
-
|
|
78
|
-
|
|
78
|
+
strict_1.default.deepEqual(loaded[0], deserialized);
|
|
79
|
+
strict_1.default.deepEqual(loaded.length, 1);
|
|
79
80
|
repository.deleteCredential(deserialized);
|
|
80
81
|
const reLoaded = repository.loadCredentials();
|
|
81
|
-
|
|
82
|
+
strict_1.default.deepEqual(reLoaded.length, 0);
|
|
82
83
|
});
|
|
83
|
-
(0,
|
|
84
|
+
(0, node_test_1.test)("File Repository Test", async () => {
|
|
84
85
|
const FILE_IO_WAIT = 500;
|
|
85
86
|
const deserialized = (0, credentials_repository_1.deserializeCredential)(JSON.stringify(testCredentialJSON));
|
|
86
87
|
const TEST_CREDENTIALS_DIR = node_path_1.default.join(__dirname, "test-credentials");
|
|
@@ -90,14 +91,14 @@ const credentials_repository_1 = require("../../src/repository/credentials-repos
|
|
|
90
91
|
repository.saveCredential(deserialized);
|
|
91
92
|
await new Promise((resolve) => setTimeout(resolve, FILE_IO_WAIT));
|
|
92
93
|
const loaded = repository.loadCredentials();
|
|
93
|
-
|
|
94
|
-
|
|
94
|
+
strict_1.default.deepEqual(loaded[0], deserialized);
|
|
95
|
+
strict_1.default.deepEqual(loaded.length, 1);
|
|
95
96
|
repository.deleteCredential(deserialized);
|
|
96
97
|
await new Promise((resolve) => setTimeout(resolve, FILE_IO_WAIT));
|
|
97
98
|
// create dummy file
|
|
98
99
|
fs.writeFileSync(node_path_1.default.join(TEST_CREDENTIALS_DIR, "dummy"), "dummy");
|
|
99
100
|
const reLoaded = repository.loadCredentials();
|
|
100
|
-
|
|
101
|
+
strict_1.default.deepEqual(reLoaded.length, 0);
|
|
101
102
|
}
|
|
102
103
|
finally {
|
|
103
104
|
fs.rmSync(TEST_CREDENTIALS_DIR, { recursive: true, force: true });
|
|
@@ -3,7 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const
|
|
6
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
7
|
+
const node_test_1 = require("node:test");
|
|
7
8
|
const authenticator_emulator_1 = require("../../src/authenticator/authenticator-emulator");
|
|
8
9
|
const encode_utils_1 = __importDefault(require("../../src/libs/encode-utils"));
|
|
9
10
|
const unit_test_1 = require("../../src/test-utils/unit-test");
|
|
@@ -12,7 +13,7 @@ const globalScope = globalThis;
|
|
|
12
13
|
const originalLocation = globalScope.location;
|
|
13
14
|
const originalBtoa = globalScope.btoa;
|
|
14
15
|
const originalAtob = globalScope.atob;
|
|
15
|
-
(0,
|
|
16
|
+
(0, node_test_1.before)(() => {
|
|
16
17
|
if (!globalScope.btoa) {
|
|
17
18
|
globalScope.btoa = (data) => Buffer.from(data, "binary").toString("base64");
|
|
18
19
|
}
|
|
@@ -26,35 +27,35 @@ const clearDefaultRepository = () => {
|
|
|
26
27
|
repository.deleteCredential(credential);
|
|
27
28
|
});
|
|
28
29
|
};
|
|
29
|
-
(0,
|
|
30
|
+
(0, node_test_1.beforeEach)(() => {
|
|
30
31
|
clearDefaultRepository();
|
|
31
32
|
});
|
|
32
|
-
(0,
|
|
33
|
+
(0, node_test_1.afterEach)(() => {
|
|
33
34
|
globalScope.location = originalLocation;
|
|
34
35
|
clearDefaultRepository();
|
|
35
36
|
});
|
|
36
|
-
(0,
|
|
37
|
+
(0, node_test_1.after)(() => {
|
|
37
38
|
globalScope.location = originalLocation;
|
|
38
39
|
globalScope.btoa = originalBtoa;
|
|
39
40
|
globalScope.atob = originalAtob;
|
|
40
41
|
});
|
|
41
|
-
(0,
|
|
42
|
-
(0,
|
|
42
|
+
(0, node_test_1.describe)("createPasskeysEmulator", () => {
|
|
43
|
+
(0, node_test_1.test)("exposes default passkeys-like interfaces", async () => {
|
|
43
44
|
const emulator = (0, unit_test_1.createPasskeysEmulator)();
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
strict_1.default.equal(await emulator.methods.publicKeyCredentials.isConditionalMediationAvailable?.(), true);
|
|
46
|
+
strict_1.default.deepEqual(await emulator.methods.publicKeyCredentials.getClientCapabilities(), { conditionalGet: true });
|
|
47
|
+
strict_1.default.equal(await emulator.methods.publicKeyCredentials.isUserVerifyingPlatformAuthenticatorAvailable(), true);
|
|
48
|
+
strict_1.default.equal(emulator.methods.publicKeyCredentials.parseCreationOptionsFromJSON, webauthn_model_json_1.parseCreationOptionsFromJSON);
|
|
49
|
+
strict_1.default.equal(emulator.methods.publicKeyCredentials.parseRequestOptionsFromJSON, webauthn_model_json_1.parseRequestOptionsFromJSON);
|
|
49
50
|
});
|
|
50
|
-
(0,
|
|
51
|
+
(0, node_test_1.test)("addPasskey stores credentials for custom rpId and allows authentication", async () => {
|
|
51
52
|
globalScope.location = { origin: "https://example.com" };
|
|
52
53
|
const emulator = (0, unit_test_1.createPasskeysEmulator)({ origin: "https://example.com", rpId: "example.com" });
|
|
53
54
|
emulator.addPasskey("user-123");
|
|
54
55
|
const repository = emulator.instance.authenticator.params.credentialsRepository;
|
|
55
56
|
const credentials = repository?.loadCredentials() ?? [];
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
strict_1.default.equal(credentials.length, 1);
|
|
58
|
+
strict_1.default.equal(credentials[0].publicKeyCredentialSource.rpId.value, "example.com");
|
|
58
59
|
const credentialId = encode_utils_1.default.encodeBase64Url(credentials[0].publicKeyCredentialSource.id);
|
|
59
60
|
const requestOptions = emulator.methods.publicKeyCredentials.parseRequestOptionsFromJSON({
|
|
60
61
|
challenge: encode_utils_1.default.encodeBase64Url(Buffer.from("challenge")),
|
|
@@ -64,11 +65,11 @@ const clearDefaultRepository = () => {
|
|
|
64
65
|
const assertion = (await emulator.methods.credentialsContainer.get({
|
|
65
66
|
publicKey: requestOptions,
|
|
66
67
|
}));
|
|
67
|
-
|
|
68
|
+
strict_1.default.equal(assertion.id, credentialId);
|
|
68
69
|
const userHandle = assertion.response.userHandle;
|
|
69
|
-
|
|
70
|
+
strict_1.default.equal(encode_utils_1.default.encodeBase64Url(new Uint8Array(userHandle ?? new ArrayBuffer(0))), encode_utils_1.default.encodeBase64Url(Buffer.from("user-123")));
|
|
70
71
|
});
|
|
71
|
-
(0,
|
|
72
|
+
(0, node_test_1.test)("creationException bubbles up as DOMException", async () => {
|
|
72
73
|
globalScope.location = { origin: "http://localhost" };
|
|
73
74
|
const emulator = (0, unit_test_1.createPasskeysEmulator)({ creationException: "AbortError" });
|
|
74
75
|
const creationOptions = emulator.methods.publicKeyCredentials.parseCreationOptionsFromJSON({
|
|
@@ -82,16 +83,16 @@ const clearDefaultRepository = () => {
|
|
|
82
83
|
pubKeyCredParams: [{ alg: -7, type: "public-key" }],
|
|
83
84
|
});
|
|
84
85
|
const createCall = emulator.methods.credentialsContainer.create({ publicKey: creationOptions });
|
|
85
|
-
await
|
|
86
|
-
await
|
|
86
|
+
await strict_1.default.rejects(createCall, DOMException);
|
|
87
|
+
await strict_1.default.rejects(createCall, { name: "AbortError" });
|
|
87
88
|
});
|
|
88
|
-
(0,
|
|
89
|
+
(0, node_test_1.test)("requestException is thrown when fetching assertions", async () => {
|
|
89
90
|
globalScope.location = { origin: "http://localhost" };
|
|
90
91
|
const emulator = (0, unit_test_1.createPasskeysEmulator)({ requestException: "InvalidStateError" });
|
|
91
92
|
emulator.addPasskey("auth-user");
|
|
92
93
|
const repository = emulator.instance.authenticator.params.credentialsRepository;
|
|
93
94
|
const credentials = repository?.loadCredentials() ?? [];
|
|
94
|
-
|
|
95
|
+
strict_1.default.equal(credentials.length, 1);
|
|
95
96
|
const credentialId = encode_utils_1.default.encodeBase64Url(credentials[0].publicKeyCredentialSource.id);
|
|
96
97
|
const requestOptions = emulator.methods.publicKeyCredentials.parseRequestOptionsFromJSON({
|
|
97
98
|
challenge: encode_utils_1.default.encodeBase64Url(Buffer.from("request")),
|
|
@@ -99,18 +100,18 @@ const clearDefaultRepository = () => {
|
|
|
99
100
|
allowCredentials: [{ type: "public-key", id: credentialId }],
|
|
100
101
|
});
|
|
101
102
|
const parsedId = encode_utils_1.default.encodeBase64Url(new Uint8Array(requestOptions.allowCredentials?.[0].id ?? new ArrayBuffer(0)));
|
|
102
|
-
(
|
|
103
|
+
strict_1.default.equal(parsedId, credentialId);
|
|
103
104
|
try {
|
|
104
105
|
await emulator.methods.credentialsContainer.get({ publicKey: requestOptions });
|
|
105
106
|
throw new Error("Expected DOMException");
|
|
106
107
|
}
|
|
107
108
|
catch (error) {
|
|
108
|
-
|
|
109
|
-
|
|
109
|
+
strict_1.default.ok(error instanceof DOMException);
|
|
110
|
+
strict_1.default.equal(error.name, "InvalidStateError");
|
|
110
111
|
}
|
|
111
112
|
});
|
|
112
|
-
(0,
|
|
113
|
+
(0, node_test_1.test)("disables conditional mediation when autofill flag is false", async () => {
|
|
113
114
|
const emulator = (0, unit_test_1.createPasskeysEmulator)({ autofill: false });
|
|
114
|
-
await
|
|
115
|
+
strict_1.default.equal(await emulator.methods.publicKeyCredentials.isConditionalMediationAvailable?.(), false);
|
|
115
116
|
});
|
|
116
117
|
});
|
|
@@ -36,49 +36,50 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
39
40
|
const crypto = __importStar(require("node:crypto"));
|
|
40
|
-
const
|
|
41
|
+
const node_test_1 = require("node:test");
|
|
41
42
|
const src_1 = require("../../src");
|
|
42
43
|
const encode_utils_1 = __importDefault(require("../../src/libs/encode-utils"));
|
|
43
|
-
(0,
|
|
44
|
-
(0,
|
|
44
|
+
(0, node_test_1.describe)("COSEKey Tests", () => {
|
|
45
|
+
(0, node_test_1.test)("RSA Key test", () => {
|
|
45
46
|
const keys = crypto.generateKeyPairSync("rsa", { modulusLength: 2048 });
|
|
46
47
|
const coseKey1 = src_1.CoseKey.fromKeyObject(keys.publicKey);
|
|
47
48
|
const coseKey2 = src_1.CoseKey.fromDer(coseKey1.toDer());
|
|
48
49
|
const coseKey3 = src_1.CoseKey.fromBytes(coseKey1.toBytes());
|
|
49
50
|
const coseKey4 = src_1.CoseKey.fromJwk(coseKey1.toJwk());
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
strict_1.default.ok(coseKey1.equals(coseKey2));
|
|
52
|
+
strict_1.default.ok(coseKey1.equals(coseKey3));
|
|
53
|
+
strict_1.default.ok(coseKey1.equals(coseKey4));
|
|
53
54
|
});
|
|
54
|
-
(0,
|
|
55
|
+
(0, node_test_1.test)("P256 Key test", () => {
|
|
55
56
|
const keys = crypto.generateKeyPairSync("ec", { namedCurve: "P-256" });
|
|
56
57
|
const coseKey1 = src_1.CoseKey.fromKeyObject(keys.publicKey);
|
|
57
58
|
const coseKey2 = src_1.CoseKey.fromDer(coseKey1.toDer());
|
|
58
59
|
const coseKey3 = src_1.CoseKey.fromBytes(coseKey1.toBytes());
|
|
59
60
|
const coseKey4 = src_1.CoseKey.fromJwk(coseKey1.toJwk());
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
strict_1.default.ok(coseKey1.equals(coseKey2));
|
|
62
|
+
strict_1.default.ok(coseKey1.equals(coseKey3));
|
|
63
|
+
strict_1.default.ok(coseKey1.equals(coseKey4));
|
|
63
64
|
});
|
|
64
|
-
(0,
|
|
65
|
+
(0, node_test_1.test)("Ed25519 Key test", () => {
|
|
65
66
|
const keys = crypto.generateKeyPairSync("ed25519");
|
|
66
67
|
const coseKey1 = src_1.CoseKey.fromKeyObject(keys.publicKey);
|
|
67
68
|
const coseKey2 = src_1.CoseKey.fromDer(coseKey1.toDer());
|
|
68
69
|
const coseKey3 = src_1.CoseKey.fromBytes(coseKey1.toBytes());
|
|
69
70
|
const coseKey4 = src_1.CoseKey.fromJwk(coseKey1.toJwk());
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
strict_1.default.ok(coseKey1.equals(coseKey2));
|
|
72
|
+
strict_1.default.ok(coseKey1.equals(coseKey3));
|
|
73
|
+
strict_1.default.ok(coseKey1.equals(coseKey4));
|
|
73
74
|
});
|
|
74
|
-
(0,
|
|
75
|
+
(0, node_test_1.test)("Unsupported key type test", () => {
|
|
75
76
|
const keys = crypto.generateKeyPairSync("rsa", { modulusLength: 2048 });
|
|
76
77
|
const unsupportedJwk = {
|
|
77
78
|
...src_1.CoseKey.fromKeyObject(keys.publicKey).toJwk(),
|
|
78
79
|
kty: "unsupported",
|
|
79
80
|
};
|
|
80
|
-
|
|
81
|
+
strict_1.default.throws(() => src_1.CoseKey.fromJwk(unsupportedJwk), { message: "Not supported key type" });
|
|
81
82
|
const unsupportedBytes = encode_utils_1.default.encodeCbor({ 1: 123, 3: 456 });
|
|
82
|
-
|
|
83
|
+
strict_1.default.throws(() => src_1.CoseKey.fromBytes(unsupportedBytes), { message: "Not supported key type" });
|
|
83
84
|
});
|
|
84
85
|
});
|